#include "precomp.h" #include typedef NTKERNELAPI NTSTATUS(__stdcall* fpPsSetCreateProcessNotifyRoutineEx)(__in PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, __in BOOLEAN Remove); typedef struct _PROCESS_INFO_ENTRY { RTL_BALANCED_LINKS Links; // AVL Æ®¸® ³ëµå ¿¬°á¿ë (Çʼö) HANDLE ProcessId; // Key (PID´Â 64ºñÆ®¿¡¼­ HANDLE·Î °ü¸®ÇÏ´Â°Ô ¾ÈÀü) WCHAR ProcessName[260]; // Value 1 WCHAR ProcessPath[512]; // Value 2 } PROCESS_INFO_ENTRY, * PPROCESS_INFO_ENTRY; // Àü¿ª º¯¼ö static RTL_AVL_TABLE g_ProcessTable; static KSPIN_LOCK g_TableLock; static BOOLEAN g_IsTableInitialized = FALSE; static NTSTATUS s_ntstatus = STATUS_UNSUCCESSFUL; static BOOLEAN bIsVistaLater = FALSE; static fpPsSetCreateProcessNotifyRoutineEx pPsSetCreateProcessNotifyRoutineEx = NULL; // ºñ±³ ÇÔ¼ö (std::less¿Í À¯»ç) RTL_GENERIC_COMPARE_RESULTS NTAPI ProcessTableCompare( _In_ PRTL_AVL_TABLE Table, _In_ PVOID FirstStruct, _In_ PVOID SecondStruct ) { UNREFERENCED_PARAMETER(Table); PPROCESS_INFO_ENTRY p1 = (PPROCESS_INFO_ENTRY)FirstStruct; PPROCESS_INFO_ENTRY p2 = (PPROCESS_INFO_ENTRY)SecondStruct; // ÇÚµé(PID) °ª ÀÚü¸¦ ºñ±³ if (p1->ProcessId < p2->ProcessId) { return GenericLessThan; } else if (p1->ProcessId > p2->ProcessId) { return GenericGreaterThan; } return GenericEqual; } // ¸Þ¸ð¸® ÇÒ´ç ÇÔ¼ö (new¿Í À¯»ç) PVOID NTAPI ProcessTableAllocate( _In_ PRTL_AVL_TABLE Table, _In_ CLONG ByteSize ) { UNREFERENCED_PARAMETER(Table); // NonPagedPool¿¡¼­ ¸Þ¸ð¸® ÇÒ´ç (Tag: 'proc') return ExAllocatePoolWithTag(NonPagedPool, ByteSize, 'proc'); } // ¸Þ¸ð¸® ÇØÁ¦ ÇÔ¼ö (delete¿Í À¯»ç) VOID NTAPI ProcessTableFree( _In_ PRTL_AVL_TABLE Table, _In_ PVOID Buffer ) { UNREFERENCED_PARAMETER(Table); ExFreePoolWithTag(Buffer, 'proc'); } // ============================================================= // [3] Map °ü¸® ÇÔ¼ö (Init, Add, Find, Remove) // ============================================================= // ÃʱâÈ­ VOID InitProcessMap() { if (!g_IsTableInitialized) { KeInitializeSpinLock(&g_TableLock); // AVL Å×À̺í ÃʱâÈ­ (Äݹé ÇÔ¼ö µî·Ï) RtlInitializeGenericTableAvl( &g_ProcessTable, ProcessTableCompare, ProcessTableAllocate, ProcessTableFree, NULL ); g_IsTableInitialized = TRUE; } } // Á¤¸® (¸ðµç ³ëµå »èÁ¦) VOID CleanupProcessMap() { if (g_IsTableInitialized) { KIRQL oldIrql; PVOID pElement; // ¶ôÀ» °É°í Çϳª¾¿ Áö¿ì°Å³ª, ´õ È¿À²ÀûÀÎ ¹æ¹ýÀº ¾Æ·¡¿Í °°½À´Ï´Ù. // µå¶óÀ̹ö ¾ð·Îµå ½Ã¿¡´Â ¶ô ¾øÀ̵µ ¾ÈÀüÇÏ´Ù¸é ¶ô Á¦°Å °¡´É KeAcquireSpinLock(&g_TableLock, &oldIrql); // Å×À̺íÀÌ ºô ¶§±îÁö ¹Ýº¹ »èÁ¦ while (!RtlIsGenericTableEmptyAvl(&g_ProcessTable)) { // ·çÆ® ³ëµå(ȤÀº ÀÓÀÇÀÇ ³ëµå)¸¦ °¡Á®¿Í¼­ »èÁ¦ pElement = RtlGetElementGenericTableAvl(&g_ProcessTable, 0); if (pElement) { RtlDeleteElementGenericTableAvl(&g_ProcessTable, pElement); } } KeReleaseSpinLock(&g_TableLock, oldIrql); g_IsTableInitialized = FALSE; } } // ÇÁ·Î¼¼½º Á¤º¸ Ãß°¡ (Insert) VOID AddProcessInfo(HANDLE ProcessId, PCUNICODE_STRING ProcessPath) { if (!g_IsTableInitialized) return; PROCESS_INFO_ENTRY newItem = { 0 }; newItem.ProcessId = ProcessId; // Key ¼³Á¤ // µ¥ÀÌÅÍ º¹»ç if (ProcessPath && ProcessPath->Buffer) { RtlStringCchCopyW(newItem.ProcessPath, 512, ProcessPath->Buffer); WCHAR* name = wcsrchr(newItem.ProcessPath, L'\\'); if (name) name++; else name = newItem.ProcessPath; RtlStringCchCopyW(newItem.ProcessName, 260, name); } else { RtlStringCchCopyW(newItem.ProcessName, 260, L"Unknown"); } KIRQL oldIrql; KeAcquireSpinLock(&g_TableLock, &oldIrql); // Å×ÀÌºí¿¡ »ðÀÔ (ÀÌ¹Ì Á¸ÀçÇÏ¸é ¾÷µ¥ÀÌÆ®Çϰųª ¹«½ÃµÊ) // RtlInsertElementGenericTableAvlÀº ³»ºÎÀûÀ¸·Î AllocateRoutineÀ» È£ÃâÇÏ¿© ¸Þ¸ð¸®¸¦ º¹»çÇÕ´Ï´Ù. // µû¶ó¼­ Áö¿ªº¯¼ö newItemÀ» ³Ñ°ÜÁ൵ ¾ÈÀüÇÕ´Ï´Ù. RtlInsertElementGenericTableAvl(&g_ProcessTable, &newItem, sizeof(PROCESS_INFO_ENTRY), NULL); SetLog(NULL, NULL, LOG_PROCESS, 1, 0, 0, newItem.ProcessName, newItem.ProcessPath); KeReleaseSpinLock(&g_TableLock, oldIrql); } // ÇÁ·Î¼¼½º Á¤º¸ »èÁ¦ (Remove) VOID RemoveProcessInfo(HANDLE ProcessId) { if (!g_IsTableInitialized) return; PROCESS_INFO_ENTRY searchKey = { 0 }; searchKey.ProcessId = ProcessId; // °Ë»öÇÒ Key¸¸ ¼³Á¤ KIRQL oldIrql; KeAcquireSpinLock(&g_TableLock, &oldIrql); // »èÁ¦ (Key¸¦ ±â¹ÝÀ¸·Î ã¾Æ¼­ »èÁ¦ÇÔ) RtlDeleteElementGenericTableAvl(&g_ProcessTable, &searchKey); KeReleaseSpinLock(&g_TableLock, oldIrql); } // PID·Î Á¤º¸ Á¶È¸ (Find) BOOLEAN GetProcessInfoByPid(HANDLE ProcessId, WCHAR* pOutName, ULONG NameLen, WCHAR* pOutPath, ULONG PathLen) { if (!g_IsTableInitialized) return FALSE; PROCESS_INFO_ENTRY searchKey = { 0 }; searchKey.ProcessId = ProcessId; BOOLEAN bFound = FALSE; KIRQL oldIrql; KeAcquireSpinLock(&g_TableLock, &oldIrql); // °Ë»ö PPROCESS_INFO_ENTRY pEntry = (PPROCESS_INFO_ENTRY)RtlLookupElementGenericTableAvl(&g_ProcessTable, &searchKey); if (pEntry) { if (pOutName) RtlStringCchCopyW(pOutName, NameLen, pEntry->ProcessName); if (pOutPath) RtlStringCchCopyW(pOutPath, PathLen, pEntry->ProcessPath); bFound = TRUE; } KeReleaseSpinLock(&g_TableLock, oldIrql); return bFound; } VOID UserNotifyEvent() { { HANDLE rptEventHandle; PKEVENT rptEvent; UNICODE_STRING name; RtlInitUnicodeString(&name, PROCESS_TERMINATE_NOTIFY_KERNEL_EVENT_NAME); rptEvent = IoCreateNotificationEvent(&name, &rptEventHandle); if (rptEvent) { KeSetEvent(rptEvent, FALSE, FALSE); ZwClose(rptEventHandle); } } } VOID TerminateProcessNotify(HANDLE ulProcessId) { //BOOLEAN Is = FALSE; ULONG state = 0; PROCESS_INFO_ENTRY newItem = { 0 }; /// ÇÁ·Î¼¼½º ¾ÆÀ̵𠸮½ºÆ® Á¦°Å state = PgRemovePid(HandleToULong(ulProcessId)); if (GetProcessInfoByPid(ulProcessId, newItem.ProcessName, ARRAYSIZE(newItem.ProcessName), newItem.ProcessPath, ARRAYSIZE(newItem.ProcessPath))) { RemoveProcessInfo(ulProcessId); KLogEx(DEBUG_TRACE_INFO, "TERMINATE pid(%d), (%S)\n", HandleToULong(ulProcessId), newItem.ProcessName); SetLog(NULL, NULL, LOG_PROCESS, 0, 0, 0, newItem.ProcessName, newItem.ProcessPath); } //if (state & (PG_PID_WHITE | PG_PID_GREEN)) //{ // /// Á¾·á ÇÁ·Î¼¼½º ¾ÆÀ̵ð µî·Ï // SetExitPid(ulProcessId); // /// À¯Àú °øÀ¯ À̺¥Æ® ½Ã±×³Î // UserNotifyEvent(); //} } VOID IsWhiteProcess(ULONG hProcessId) { UNICODE_STRING ProcessPath = { 0, }; ULONG ulType = 0; /// ÇÁ·Î¼¼½º À̸§À» ¾ò´Â´Ù. if (!NT_SUCCESS(UGetCurrentStackProcessImageName(hProcessId, &ProcessPath))) { return; } /// Á¤ÀÇµÈ Çã¿ë ÇÁ·Î¼¼½º ÀÎÁö È®ÀÎ KLogEx(DEBUG_TRACE_INFO, "ProcessCreate, Pid(%d), %S(%d)\n", hProcessId, ProcessPath.Buffer, ProcessPath.Length); if (!IsAllowProcessName(ProcessPath.Buffer, ProcessPath.Length, &ulType)) { /// Á¤ÀÇ ¾ÈµÈ ÇÁ·Î¼¼½º Á¤Ã¥¿¡ µû¸¥ Â÷´Ü PgAddPid(hProcessId, PG_PID_BLACK); } else { /// Çã¿ë ÇÁ·Î¼¼½º µî·Ï PgAddPid(hProcessId, ulType); } UStrFree(&ProcessPath); return; } typedef struct _PROCESS_MESSAGE { ULONG ProcessId; WCHAR ProcessName[260]; WCHAR ProcessPath[512]; } PROCESS_MESSAGE, * PPROCESS_MESSAGE; #define SCANNER_MESSAGE_SIZE (sizeof(FILTER_MESSAGE_HEADER) + sizeof(PROCESS_MESSAGE)) VOID CreateProcessNotifyEx(PEPROCESS pEprocess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO pCreateInfo) { UNICODE_STRING parameter = { 0, }; //NTSTATUS status = 0; UNREFERENCED_PARAMETER(pEprocess); if (!g_bs1Flt.IsAttached) return; // ÇÁ·Î¼¼½º Á¾·á if (!pCreateInfo) { //KLogEx(" Terminate %d\n", HandleToULong(hProcessId)); TerminateProcessNotify(hProcessId); } else { if (pCreateInfo->CommandLine && pCreateInfo->CommandLine->Buffer) { if (!NT_SUCCESS(UStrNew(¶meter, pCreateInfo->CommandLine->MaximumLength + 10))) { return; } UStrCopy(¶meter, (PUNICODE_STRING)pCreateInfo->CommandLine); //EXCEL.EXE" /automation -Embedding if (wcsstr(pCreateInfo->CommandLine->Buffer, L"EXCEL.EXE\" /automation -Embedding")) { PgAddPid(HandleToULong(hProcessId), PG_PID_ALLOW); } UStrFree(¶meter); } if (!g_bs1Flt.IsProcessCreate) return; PROCESS_MESSAGE msg = { 0, }; msg.ProcessId = (ULONG)(ULONG_PTR)hProcessId; //if (pCreateInfo->ImageFileName && pCreateInfo->ImageFileName->Buffer) //{ // RtlStringCchCopyW(msg.ProcessPath, 512, pCreateInfo->ImageFileName->Buffer); // wchar_t* name = wcsrchr(msg.ProcessPath, L'\\'); // if (name) // name++; // else // name = msg.ProcessPath; // RtlStringCchCopyW(msg.ProcessName, 260, name); //} //else //{ // RtlStringCchCopyW(msg.ProcessName, 260, L"Unknown"); //} //KLogEx(DEBUG_TRACE_INFO, "Create pid(%d), (%S)\n", msg.ProcessId, msg.ProcessName); // //if (g_bs1Flt.ClientPort) //{ // LARGE_INTEGER timeout; // timeout.QuadPart = (LONGLONG)-10 * 1000 * 1000; //3 seconds // // status = FltSendMessage(g_bs1Flt.Filter, &g_bs1Flt.ClientPort, &msg, sizeof(msg), NULL, NULL, &timeout); // KLogEx(DEBUG_TRACE_ERROR, "FltSendMessage, status(%x)\n", status); //} if (pCreateInfo->ImageFileName && pCreateInfo->ImageFileName->Buffer) { if (!NT_SUCCESS(UStrNew(¶meter, pCreateInfo->ImageFileName->MaximumLength + 10))) return; UStrCopy(¶meter, (PUNICODE_STRING)pCreateInfo->ImageFileName); KLogEx(DEBUG_TRACE_INFO, "CREATE pid(%d), (%S)\n", HandleToULong(hProcessId), parameter.Buffer); AddProcessInfo(hProcessId, ¶meter); UStrFree(¶meter); } // // IsWhiteProcess(hProcessId); } } VOID CreateProcessNotify(IN HANDLE ParentId, IN HANDLE hProcessId, IN BOOLEAN Create) { UNREFERENCED_PARAMETER(ParentId); if (!g_bs1Flt.IsAttached) { return; } if (Create == FALSE) { //KLogEx(" Terminate %d\n", HandleToULong(hProcessId)); TerminateProcessNotify(hProcessId); } else { // »ý¼ºµÈ ÇÁ·Î¼¼½º üũ //KLogEx(" Create %d\n", HandleToULong(hProcessId)); // IsWhiteProcess(hProcessId); } } NTSTATUS InitProcessNotify() { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING funcname = { 0, }; InitProcessMap(); RtlInitUnicodeString(&funcname, L"PsSetCreateProcessNotifyRoutineEx"); pPsSetCreateProcessNotifyRoutineEx = (fpPsSetCreateProcessNotifyRoutineEx)MmGetSystemRoutineAddress(&funcname); if (pPsSetCreateProcessNotifyRoutineEx) { bIsVistaLater = TRUE; status = pPsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, FALSE); } else { status = PsSetCreateProcessNotifyRoutine(CreateProcessNotify, FALSE); } KLogEx(DEBUG_TRACE_INFO, "status(%x), (%p)\n", status, pPsSetCreateProcessNotifyRoutineEx); s_ntstatus = status; return status; } VOID CleanupProcessNotify() { if (bIsVistaLater) { if(NT_SUCCESS(s_ntstatus)) pPsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, TRUE); } else { if (NT_SUCCESS(s_ntstatus)) PsSetCreateProcessNotifyRoutine(CreateProcessNotify, TRUE); } CleanupProcessMap(); } DWORD GetProcessNotifyStatus() { KLogEx(DEBUG_TRACE_INFO, "status = %x\n", s_ntstatus); return s_ntstatus; }