Санкт-Петербургский государственный политехнический университет
Факультет технической кибернетики
Кафедра Компьютерные системы и программные технологии
Отчёт по лабораторной работе №4
Дисциплина: "Системное программное обеспечение"
Тема: Аудит объекта системы.
Выполнил студент гр. 5081/13 Залеский А. А.
Преподаватель __________ Душутина Е. В.
\
-
Введение
Формулировка моего задания включает слово «Аудит», дадим определение этому понятию. Аудит — процесс записи информации о происходящих с каким-то объектом (или в рамках какого-то процесса) событиях в журнал (например, в файл). Таким образом в задании требуется записывать информацию о событиях с объектом. Решение проблемы я начал с обзора продуктов, позволяющих вести аудит файлов в системе.
-
Обзор продуктов.
Интернет пестрит всевозможными программами для аудита файлов. Многие из них бесплатны, но некоторые бывают и платными. Приведу список самых популярных, на мой взгляд, программных продуктов: ChangeAuditor for Windows, File Servers, NetWrix Change Reporter Suite, Blackbird Auditor for File System,ADAudit Plus, CPTRAX for Windows, FileSure for Windows, File Audit. Есть и другие, но эти мне показались наиболее интересными. Рассмотрю же я всего один, последний. Программа File Audit абсолютно бесплатна, но, как и следовало ожидать, закрытым кодом. Внутри она предоставляет довольно скромный и очень понятный интерфейс:
Можно выбрать папку в которой будет вестись аудит всех файлов, подходящих под маску фильтра, а так же в поддиректориях, если поставлена галочка в чек боксе. На каждое из действий с файлом тоже можно поставить галочки, и есть опция сохранения аудита в файл. Не большой набор, однако, подобный предоставляют абсолютно все программы, которые я смотрел. Немного покопавшись в MSDN я нашел функцию, с помощью которой и реализовывается подобный аудит.
-
Своя реализация аудита папки.
Функция, позволяющая вести аудит всех файлов в каком-либо катологе называется ReadDirectoryChangesW(). Ниже представлено описание полей, которые она принимает:
_In_ HANDLE hDirectory, - сама директория, в которой ведется аудит
_Out_ LPVOID lpBuffer, - буфер, куда записывается информация аудита
_In_ DWORD nBufferLength, - длина этого буфера
_In_ BOOL bWatchSubtree, - флаг, указывающий нужно ли смотреть подкаталоги диектории, указанной в hDirectory
_In_ DWORD dwNotifyFilter, - тут описываются все действия, которые нужно подвергать аудиту: Запись,изменение имени, создание, удаление и т.д.
_Out_opt_ LPDWORD lpBytesReturned, - размер буфера для синхронной записи
_Inout_opt_ LPOVERLAPPED lpOverlapped, - структура, хранящая данные во время асинхронной записи
_In_opt_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine – структура необходимая при отмене аудита извне.
Как видно, все параметры, доступные в программе Audit File задаются программно в этой функции. Мной была написана программа, позволяющая отслеживать тот же функционал что и рассмотренная программа.
#define UNICODE
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
static PCSTR GetActionName(DWORD Action)
{
static const PCSTR Actions[] = { //Возможные действия с файлом
"0",
"FILE_ACTION_ADDED", // файл добавлен
"FILE_ACTION_REMOVED", // файл удален
"FILE_ACTION_MODIFIED", // файл изменен
"FILE_ACTION_RENAMED_OLD_NAME", // файл переименован из этого имени
"FILE_ACTION_RENAMED_NEW_NAME" // файл переименован в это имя
};
static CHAR Buffer[16];
if (Action >= RTL_NUMBER_OF(Actions)) {
sprintf(Buffer, "%lu", Action);
return Buffer;
}
return Actions[Action];
}
int process(void) {
WCHAR DirectoryName[MAX_PATH];
HANDLE DirectoryHandle;
OVERLAPPED Overlapped = { 0 };
FILE_NOTIFY_INFORMATION *FileNotifyInformation;
PVOID FileNotifyInformationBuffer;
DWORD FileNotifyInformationSize;
DWORD BytesReturned;
BOOL Ret;
if (!GetCurrentDirectory(RTL_NUMBER_OF(DirectoryName), DirectoryName)) { // Получаем текущую директорию, в которой расположена программа
fprintf(stderr, "GetCurrentDirectory failed with %lu\n", GetLastError());
return EXIT_FAILURE;
}
DirectoryHandle = CreateFile(DirectoryName, // Узнаем хэндл текущей директории.
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (DirectoryHandle == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFile failed with %lu\n", GetLastError());
return EXIT_FAILURE;
}
Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Создаем обработчик событий
if (Overlapped.hEvent == NULL) {
fprintf(stderr, "CreateEvent failed with %lu\n", GetLastError());
CloseHandle(DirectoryHandle);
return EXIT_FAILURE;
}
FileNotifyInformationSize = 2 * FIELD_OFFSET(FILE_NOTIFY_INFORMATION,FileName[MAX_PATH]); // создаем необходимые поля для функции ReadDirectoryChangesW
FileNotifyInformationBuffer = HeapAlloc(GetProcessHeap(),0,FileNotifyInformationSize); // создаем необходимые поля для функции ReadDirectoryChangesW
if (FileNotifyInformationBuffer == NULL) {
fprintf(stderr, "Failed to allocate %lu bytes\n", FileNotifyInformationSize);
CloseHandle(Overlapped.hEvent);
CloseHandle(DirectoryHandle);
return EXIT_FAILURE;
}
printf("Watching directory '%ls' for changes...\n", DirectoryName);
while (1) { // Бесконечный цикл, потому что остановка программы должна происходить пользователем извне
Ret = ReadDirectoryChangesW(DirectoryHandle, // Создаем листенер изменений в директории, который отлавливает все возможные действия с файлами в каталоге и всех подкаталогах. Делает он это асинхронно.
FileNotifyInformationBuffer,
FileNotifyInformationSize,
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
| FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_ATTRIBUTES,
NULL,
&Overlapped,
NULL);
if (!Ret) {
fprintf(stderr, "ReadDirectoryChangesW failed with %lu\n", GetLastError());
HeapFree(GetProcessHeap(), 0, FileNotifyInformationBuffer);
CloseHandle(Overlapped.hEvent);
CloseHandle(DirectoryHandle);
return EXIT_FAILURE;
}
Ret = GetOverlappedResult(DirectoryHandle, // Ожидаем событий, порождаемых изменением в директории
&Overlapped,
&BytesReturned,
TRUE);
if (!Ret) {
fprintf(stderr, "GetOverlappedResult failed with %lu\n", GetLastError());
HeapFree(GetProcessHeap(), 0, FileNotifyInformationBuffer);
CloseHandle(Overlapped.hEvent);
CloseHandle(DirectoryHandle);
return EXIT_FAILURE;
}
FileNotifyInformation = (FILE_NOTIFY_INFORMATION *)FileNotifyInformationBuffer;
BytesReturned = min(BytesReturned, FileNotifyInformationSize);
while (BytesReturned >= RTL_SIZEOF_THROUGH_FIELD(FILE_NOTIFY_INFORMATION, FileNameLength) &&
BytesReturned >= FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName[FileNotifyInformation->FileNameLength / sizeof(WCHAR)])) { // При получении события, обрабатываем сообщение о совершенном событии. Извлекаем из структуры FileNotifyInformation имя файла, с которым связано действие и само совершенное действие
printf("File '%.*ls' has seen an action of type %s\n",
(int)FileNotifyInformation->FileNameLength / sizeof(WCHAR),
FileNotifyInformation->FileName,
GetActionName(FileNotifyInformation->Action));
if (FileNotifyInformation->NextEntryOffset == 0)
break;
if (BytesReturned < FileNotifyInformation->NextEntryOffset)
break;
BytesReturned -= FileNotifyInformation->NextEntryOffset; // Если событие произошло не одно, то обрабатываем всю очередь.
FileNotifyInformation = (FILE_NOTIFY_INFORMATION *)((ULONG_PTR)FileNotifyInformation + FileNotifyInformation->NextEntryOffset);
}
ZeroMemory(&Overlapped, FIELD_OFFSET(OVERLAPPED, hEvent));
ResetEvent(Overlapped.hEvent);
}
HeapFree(GetProcessHeap(), 0, FileNotifyInformationBuffer);
CloseHandle(Overlapped.hEvent);
CloseHandle(DirectoryHandle);
return EXIT_SUCCESS;
}
void main(int argc, TCHAR *argv[]){
int result = process();
int a;
scanf_s("%d", &a);
}
Программа отображает в консоли все действия в указанной директории:
Так как при создании файл имеет имя «Новый файл», то операция создания не отобразилась до конца(Видно лишь начало строки “File ` “) – не подключал русские символы в консоли.
-
Аудит файлов с помощью Windows
Как бы хорошо и быстро ни работал описанный выше метод, он имеет два огромных недостатка: 1) Программа способна собирать информацию об аудите лишь тогда, когда включена. 2) Программа не может отследить открытие и закрытие файла, а так же его копирование в другое место. В связи с этим, я попробовал зайти с другого конца – через встроенную в систему windows возможность аудита файлов. Каждый файл в windows кроме своего содержания хранит еще важную информацию для обеспечения безопастности. Устройство это информации показано на рисунке. Меня интересуют последние два поля – указатель на DACL и SACL. Разберемся что это такое. Список управления избирательным доступом (discretionary access-control list, DACL)Указывает, кто может получать доступ к объекту и какие виды доступа. Системный список управления доступом (system access-control list, SACL) Указывает, какие операции и каких пользователей должны регистрироваться в журнале аудита безопасности. Эти понятия объединяются общим термином ACL. Список управления доступом(access-control list, ACL) состоит из заголовка и может содержать элементы (access-control entries, АСЕ). B DACL каждый ACE содержит SID и маску доступа (а также набор флагов), причем ACE могут быть четырех типов: «доступ разрешен» (access allowed), «доступ отклонен» (access denied), «разрешенный объект» (allowed-object) и «запрещенный объект» (denied-object). Первый тип ACE разрешает пользователю доступ к объекту, а второй – отказывает в предоставлении прав, указанных в маске доступа.
Разница между ACE типа «разрешенный объект» и «доступ разрешен», а также между ACE типа «запрещенный объект» и «доступ отклонен» заключается в том, что эти типы используются только в Active Directory. ACE этих типов имеют поле глобально уникального идентификатора (globally unique identifier, GUID), которое сообщает, что данный ACE применим только к определенным объектам или под объектам (с GUID-идентификаторами). Кроме того, необязательный GUID указывает, что тип дочернего объекта наследует ACE при его (объекта) создании в контейнере Active Directory, к которому применен АСЕ. (GUID – это гарантированно уникальный 128-битный идентификатор.)
За счет аккумуляции прав доступа, сопоставленных с индивидуальными АСЕ, формируется набор прав, предоставляемых ACL-списком. Если в дескрипторе защиты нет DACL (DACL = null), любой пользователь получает полный доступ к объекту. Если DACL пуст (т. е. в нем нет АСЕ), доступа к объекту не получает никто.
SACL состоит из ACE двух типов: системного аудита (system audit ACE) и объекта системного аудита (system audit-object АСЕ). Эти ACE определяют, какие операции, выполняемые над объектами конкретными пользователями или группами, подлежат аудиту. Информация аудита хранится в системном журнале аудита. Аудиту могут подлежать как успешные, так и неудачные операции. Как и специфические для объектов ACE из DACL, ACE объектов системного аудита содержат GUID, указывающий типы объектов или под-объектов, к которым применим данный АСЕ, и необязательный GUID, контролирующий передачу ACE дочерним объектам конкретных типов. При SACL, равном null, аудит объекта не ведется. Флаги наследования, применимые к DACL АСЕ, применимы к ACE системного аудита и объектов системного аудита.
Разобравшись как устроен аудит файлов с помощью встроенных в windows опций, я начал искать как их можно включить. На сайте MSDN приводится подробная инструкция включения аудита у файла с помощью GUI windows:
Включение аудита доступа пользователей к файлам, папкам и принтерам:
-
Информационные записи системного аудита заносятся в журнал событий "Безопасность". Для включения системного аудита выполните следующие действия: В меню "Пуск" выберите пункт "Панель управления", щелкните по ссылке "Производительность и обслуживание" и откройте группу программ "Администрирование".
-
Откройте элемент "Локальная политика безопасности".
-
Разверните элемент "Локальные политики".
-
Выберите папку "Политика аудита".
-
Двойным щелчком мыши откройте параметр "Аудит доступа к объектам".
-
Для отслеживания удачных попыток доступа к файлам, папкам и принтерам установите флажок "Успех".
-
Для отслеживания неудачных попыток доступа к файлам, папкам и принтерам установите флажок "Отказ".
-
Для отслеживания всех попыток доступа к объектам аудита установите оба флажка.
-
Нажмите кнопку "ОК".
Проделав все действия вручную, я действительно добился включения аудита у файла. Однако если задача стоит во включении аудита у всех файлов определенного типа? Или у всех файлов в папке? Щелкать по каждому и вручную проводить эту не самую простую операцию затруднительно. Поэтому я решил написать программу, позволяющую включать и выключать аудит у любого файла.
-
Программная реализация включения/выключения аудита windows.
Что бы в системе можно было включить аудит файла, необходимо в политике безопасности включить соответствующую привилегию. Это можно сделать вручную, запустив «Локальные параметры безопасности» из меню «Администрирование». Выбрать «Локальные политики» → «Политики Аудита» и поставив в «параметре безопасности» Успех/Отказ. Можно так же это сделать и программно, использовав функцию LsaSetInformationPolicy().
NTSTATUS LsaSetInformationPolicy(
_In_ LSA_HANDLE PolicyHandle, - Хендл текущей политики безопастности. Берется из функции LsaOpenPolicy().
_In_ POLICY_INFORMATION_CLASS InformationClass, - Определяет один класс локальной политики, для которого нужно изменить параметр безопасности. В моем случае он равен PolicyAuditEventsInformation
_In_ PVOID Buffer – буфер, в который определенным образом записываются данные, о том какие из политик аудита включить.
);
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <ntsecapi.h>
#pragma hdrstop
int IsAuditOn( BOOL forceAuditOn )
{
int rc = 0;
POLICY_ACCOUNT_DOMAIN_INFO *ppadi = NULL;
SECURITY_QUALITY_OF_SERVICE sqos;
LSA_OBJECT_ATTRIBUTES lsaOA;
LSA_HANDLE polHandle;
NTSTATUS nts;
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sqos.ImpersonationLevel = SecurityImpersonation;
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
sqos.EffectiveOnly = FALSE;
lsaOA.Length = sizeof(LSA_OBJECT_ATTRIBUTES); //формируем структуру доступа к политике безопасности
lsaOA.RootDirectory = NULL;
lsaOA.ObjectName = NULL;
lsaOA.Attributes = 0;
lsaOA.SecurityDescriptor = NULL;
lsaOA.SecurityQualityOfService = &sqos;
nts = LsaOpenPolicy( возвращает хендл, указывающий на структуру политики безопасности windows на текущем компьютере.
NULL, // NULL = current machine.
&lsaOA,
POLICY_VIEW_LOCAL_INFORMATION | GENERIC_READ | GENERIC_EXECUTE |
POLICY_ALL_ACCESS,
&polHandle);
if (nts != 0) return -1;
nts = LsaQueryInformationPolicy( //По полученному хэндлу, получаем саму структуру политики безопасности, записываемую в ppadi
polHandle,
PolicyAuditEventsInformation,
(PVOID*) &ppadi);
if (nts != 0) return -1;
if ( forceAuditOn )
{
// Устанавливаем все нужные политики в положение enable.
ppadi->DomainName.Buffer[0] = 3; // restart_shutdown_and_system
ppadi->DomainName.Buffer[2] = 3; // logon_and_logoff
ppadi->DomainName.Buffer[4] = 3; // file_and_object_access
ppadi->DomainName.Buffer[6] = 3; // use_of_user_rights
ppadi->DomainName.Buffer[8] = 3; // process_tracking
ppadi->DomainName.Buffer[10] = 3; // security_policy_changes
ppadi->DomainName.Buffer[12] = 3; // user_and_group_management
ppadi->DomainName.Length = 1;
nts = LsaSetInformationPolicy( // Устанавливаем обновленную политику безопасности
polHandle,
PolicyAuditEventsInformation,
ppadi);
if (nts != 0) return -1;
rc = 1;
}
LsaFreeMemory(polHandle); //Освобождаем память
return rc;
}
int _tmain(int argc, _TCHAR* argv[])
{
int rc;
rc = IsAuditOn( TRUE );
if ( rc == 1 )
puts( "Auditing has been enabled." );
else if ( rc == 0 )
puts( "The audit state is unchanged." );
else
puts( "Oops!" );
return 0;
}
После того, как включена возможность записи аудита в журнал безопастности. осталось лишь включить аудит какого-либо файла. Однако сделать это не так просто. Для начала нужно получить все данные о файле(security information), включая SID владельца, DACL , SACL и прочую информацию. Делается это с помощью функции GetNamedSecurityInfo()
DWORD WINAPI GetNamedSecurityInfo(
_In_ LPTSTR pObjectName, - путь до объекта.
_In_ SE_OBJECT_TYPE ObjectType, - тип объекта. В нашем случае объект может быть любым, поэтому определяем его тип функцией DetermineObjectTypeFromPath
_In_ SECURITY_INFORMATION SecurityInfo, - набор бит, определяющий какую именно информацию нам необходимо получить
_Out_opt_ PSID *ppsidOwner, - SID владельца объекта
_Out_opt_ PSID *ppsidGroup, - SID группы объекта
_Out_opt_ PACL *ppDacl, - Указатель на структуру DACL
_Out_opt_ PACL *ppSacl, - Указатель на структуру SACL
_Out_opt_ PSECURITY_DESCRIPTOR *ppSecurityDescriptor – указатель на всю структуру security information, включающей все прошлые пункты.
);
После того, как мы получили security information о объекте, нужно вычленить старый SACL объекта, что бы к нему приписать новое ACE. Получается от с помощью функции GetAclInformation()
BOOL WINAPI GetAclInformation(
_In_ PACL pAcl, - Указатель на структуру SACL из GetNamedSecurityInfo
_Out_ LPVOID pAclInformation, - буфер, куда будет помещена информация
_In_ DWORD nAclInformationLength, - длина буфера
_In_ ACL_INFORMATION_CLASS dwAclInformationClass – определяет вид получения данных. Может принимать всего 2 варианта, в моем случае, AclSizeInformation
);
Затем определяем новый SACL с помощбю функции InitializeAcl()
BOOL WINAPI InitializeAcl(
_Out_ PACL pAcl, - указатель на созданную структуру
_In_ DWORD nAclLength, - размер буфера для новой структуры
_In_ DWORD dwAclRevision – уровень ревизии
);
Теперь добавляем к созданной структуре SACL все старые ACE из полученного ранее старого SACL( функция GetAce() ) А так же новый ACE. Функция добавления ACE: AddAce()
(Разберу одну, потому что они очень похожи)
BOOL WINAPI AddAce(
_Inout_ PACL pAcl, - указатель на созданную структуру
_In_ DWORD dwAceRevision, - уровень ревизии
_In_ DWORD dwStartingAceIndex, - позиция в SACL на которую добавтся новый ACE
_In_ LPVOID pAceList, - Самое важное – параметры нового ACE
_In_ DWORD nAceListLength – и длина листа с параметрами ACE
);
Наконец у нас готов новый SACL и мы можем его добавить к нашей security information! Добавляем функцией SetNamedSecurityInfo(). Она абсолютна аналогична функции GetNamedSecurityInfo() и я не стану снова ее разбирать.
А вот и сама функция добавления аудита к файлу:
int CNTFS::AddACEToSACL(CString & I_objPath,
CString & I_securityPrincipal,
DWORD I_objPermission,
BOOL I_auditSuccess,
BOOL I_auditFailure)
{
int returnCode = ERROR_SUCCESS;
BOOL isUser = TRUE;
UCHAR BuffSid[256];
PSID pSID = (PSID)BuffSid;
PACL pOldSACL = NULL;
PACL pNewSACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_INFORMATION ACLSecInfo = SACL_SECURITY_INFORMATION;
ACL_SIZE_INFORMATION ACLInfo;
SE_OBJECT_TYPE SEObjType = SE_UNKNOWN_OBJECT_TYPE;
memset(&ACLInfo, 0, sizeof(ACL_SIZE_INFORMATION));
if (I_objPath == "")
{
returnCode = ERROR_INVALID_PARAMETER;
}
if (I_securityPrincipal == "")
{
returnCode = ERROR_INVALID_PARAMETER;
}
if (I_objPermission == 0)
{
returnCode = ERROR_INVALID_PARAMETER;
}
if (returnCode == ERROR_SUCCESS)
{
returnCode = ResolveSID(I_securityPrincipal, pSID, isUser);
}
if (returnCode == ERROR_SUCCESS)
{
returnCode = AdjustToken();
}
if (returnCode == ERROR_SUCCESS)
{
SEObjType = DetermineObjectTypeFromPath(I_objPath); // Определяем тип объекта, который лежит по указанному пути.
returnCode = GetNamedSecurityInfo( // Получаем все данные о файле
I_objPath.GetBuffer(_MAX_PATH), // object name
SEObjType, // object type
ACLSecInfo, // information type
NULL, // owner SID
NULL, // primary group SID
NULL, // DACL
&pOldSACL, // SACL
&pSD); // SD
}
// Get SACL size information
if ((returnCode == ERROR_SUCCESS) && (pOldSACL != NULL))
{
BOOL getACLResult = GetAclInformation(pOldSACL, //Выцепляем из структуры полученных данных, необходимый нам SACL
&ACLInfo,
sizeof(ACLInfo),
AclSizeInformation);
if (!getACLResult)
{
returnCode = GetLastError();
}
}
if (returnCode == ERROR_SUCCESS)
{
DWORD cb = 0;
DWORD cbExtra = 0;
if (ACLInfo.AclBytesInUse == 0)
{
cbExtra = sizeof(ACL) +
sizeof(SYSTEM_AUDIT_ACE) -
sizeof(DWORD) +
GetLengthSid(pSID);
}
else
{
cbExtra = sizeof(SYSTEM_AUDIT_ACE) - sizeof(DWORD) + GetLengthSid(pSID);
}
cb = ACLInfo.AclBytesInUse + cbExtra; //Формируем новый ACE размером равным предыдущим ACE в SACL плюс под новые записи.
pNewSACL = static_cast<PACL>(HeapAlloc(GetProcessHeap(),0,cb));
BOOL initACLResult = InitializeAcl(pNewSACL, cb, ACL_REVISION); // Инициализируем новую структуру ACL, в данном случае под SACL
if (!initACLResult)
{
returnCode = GetLastError();
}
}
if (returnCode == ERROR_SUCCESS)
{
for (DWORD i = 0; i < ACLInfo.AceCount; ++i)
{
ACE_HEADER * pACE = 0;
GetAce(pOldSACL, i, reinterpret_cast<void**>(&pACE));
pACE->AceFlags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
pACE->AceType = SYSTEM_AUDIT_ACE_TYPE;
AddAce(pNewSACL, ACL_REVISION, MAXDWORD, pACE, pACE->AceSize); // Добавляем все уже существующие ACE в проинициализированную новую структуру ACL
}
BOOL addACEResult = AddAuditAccessAceEx(pNewSACL,
ACL_REVISION,
CONTAINER_INHERIT_ACE |
OBJECT_INHERIT_ACE,
I_objPermission,
pSID,
I_auditSuccess,
I_auditFailure); // добавляем новую ACE, с параметрами заданными в структуре I_objPermission (это один из аргументов функции, задает аудит на какие действия включать) в конец структуры ACL.
if (!addACEResult)
{
returnCode = GetLastError();
}
}
if (returnCode == ERROR_SUCCESS)
{
DWORD setSIResult = SetNamedSecurityInfo( // Записываем наш только что сформированный SACL в данные безопасности файла вместо старого.
I_objPath.GetBuffer(_MAX_PATH), // object name
SEObjType, // object type
ACLSecInfo, // type
NULL, // new owner SID
NULL, // new primary group SID
NULL, // new DACL
pNewSACL); // new SACL
if (!setSIResult)
{
returnCode = setSIResult;
}
}
if (pSD != NULL)
{
LocalFree(pSD);
}
if (pNewSACL != NULL)
{
HeapFree(GetProcessHeap(),0, pNewSACL);
}
return returnCode;
}
Формат вызова функции следующий:
DWORD accessMask = FILE_ALL_ACCESS | FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE;
CString user = "1";
CString path = "D:\\Lab_1.docx";
int status = AddACEToSACL(path,
user,
accessMask,
TRUE,
TRUE);
После этого у файла D:\\Lab_1.docx появится аудит, и любые действия с ним будут отображаться в журнале безопасности windows. К примеру, открытие файла для чтения порождает событие:
-
Чтение журнала безопасности windows.
Последним, на чем мне хотелось остановить внимание – автоматическое чтение журнала windows для получения информации о конкретном файле. К сожалению, существует лишь одна winAPI функция для чтения данных из журнала безопасности: ReadEventLog()
BOOL ReadEventLog(
_In_ HANDLE hEventLog, - Хэндл, какой из журналов читать
_In_ DWORD dwReadFlags, - параметр чтения – случайно/с начала/с конца
_In_ DWORD dwRecordOffset, - Смещение при чтении
_Out_ LPVOID lpBuffer, - буфер, куда будут записываться данные.
_In_ DWORD nNumberOfBytesToRead, Размер буфера
_Out_ DWORD *pnBytesRead, - количество читаемых байт
_Out_ DWORD *pnMinNumberOfBytesNeeded – минимальное количество считываемых байт
);
Эта функция возвращает структуру EVENTLOGRECORD , содержащую следующие поля:
typedef struct _EVENTLOGRECORD {
DWORD Length;
DWORD Reserved;
DWORD RecordNumber;
DWORD TimeGenerated;
DWORD TimeWritten;
DWORD EventID;
WORD EventType;
WORD NumStrings;
WORD EventCategory;
WORD ReservedFlags;
DWORD ClosingRecordNumber;
DWORD StringOffset;
DWORD UserSidLength;
DWORD UserSidOffset;
DWORD DataLength;
DWORD DataOffset;
}
Как видно, среди них нету поля с именем объекта к которому применялось действие. Поэтому у меня не получилось отфильтровать журнал событий на предмет событий аудита с нужным файлом. Вероятно, это можно сделать каким-то образом через перехват событий, но я не разобрался с этим. На последок приведу программу, считывающую все данные из журнала безопасности windows, вкладки Security:
#include "stdafx.h"
#include <Windows.h>
#define BUFFER_SIZE 16384
void ReadAnyLog();
int _tmain(int argc, _TCHAR* argv[])
{
ReadAnyLog();
char LogName[15];
scanf("%s", LogName);
return 0;
}
void ReadAnyLog()
{
HANDLE h;
EVENTLOGRECORD *pevlr;
BYTE bBuffer[BUFFER_SIZE];
DWORD dwRead, dwNeeded, cRecords, dwThisRecord;
h = OpenEventLog( NULL, (LPCWSTR)"Security");
if (h == NULL)
printf("\n Could not open the Application event log\n");
pevlr = (EVENTLOGRECORD *) &bBuffer;
GetOldestEventLogRecord(h, &dwThisRecord);
while (ReadEventLog(h, // event log handle
EVENTLOG_FORWARDS_READ | // reads forward
EVENTLOG_SEQUENTIAL_READ, // sequential read
0, // ignored for sequential read
pevlr, // pointer to buffer
BUFFER_SIZE, // size of buffer
&dwRead, // number of bytes read
&dwNeeded)) // bytes in next record
{
while (dwRead > 0)
{
printf("%02d Event ID: 0x%08X ",
dwThisRecord++, pevlr->EventID);
printf("EventType: %d Source: %s\n",
pevlr->EventType, (LPSTR) ((LPBYTE) pevlr +
sizeof(EVENTLOGRECORD)));
dwRead -= pevlr->Length;
pevlr = (EVENTLOGRECORD *)((LPBYTE) pevlr + pevlr->Length);
}
pevlr = (EVENTLOGRECORD *) &bBuffer;
}
CloseEventLog(h);
}
-
Выводы
-
В ходе работы я прочитал много статей с различных источников. В основном, это был MSDN и книжка автора: Руссинович Марк - Книга: "3.Внутреннее устройство Windows . Почерпнул очень много интересного как о аудите в целом, так и его применимости в среде windows. Научился работать с security information объектов и узнал её структуру. Ближе познакомился с журналом безопасности windows и его профилированием. На мой взгляд работа была очень интересной, хотя и очень объемной.
Источники:
http://www.rulit.net/books/3-vnutrennee-ustrojstvo-windows-gl-8-11-read-17203-7.html
http://www.sepago.de/d/helge/2009/03/12/permissions-a-primer-or-dacl-sacl-owner-sid-and-ace-explained
http://www.codeproject.com/Articles/10042/The-Windows-Access-Control-Model-Part-1
http://www.osp.ru/win2000/2001/05/174875/
http://support.microsoft.com/kb/310399/ru
http://technet.microsoft.com/ru-ru/library/cc738931%28v=ws.10%29.aspx
http://stavkombez.ru/method/PASOIB/html/content/lab_9.html
http://stackoverflow.com/questions/1083372/listening-to-file-changes-in-c-c-on-windows
http://msdn.microsoft.com/en-us/library/aa365261%28VS.85%29.aspx
http://www.softpedia.com/get/System/System-Miscellaneous/File-Audit.shtml
http://msdn.microsoft.com/en-us/library/aa364417%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa365465%28v=vs.85%29.aspx
http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa363646%28v=vs.85%29.aspx
http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa363674%28v=vs.85%29.aspx
http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa379579%28v=vs.85%29.aspx
http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa446635%28v=vs.85%29.aspx
http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa446645%28v=vs.85%29.aspx
http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa378853%28v=vs.85%29.aspx
…. и еще множество ссылок на msdn.
Санкт-Петербург
2012г