////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Description : // Generates a message using "pid", "message" and "parameter" and sends it back to userland throught // a filter communication port. // Parameters : // _in_opt_ ULONG pid : Process ID from which the logs are produced. // _in_opt_ PWCHAR message : Message (function name most of the time). // _in_opt_ PWCHAR parameter : Function args. // Return value : // NTSTATUS : FltSendMessage return value. // Process : // - Retrieves the process name from the pid and saves the whole data into an ANSI string. // - Generates an ANSI string with the message, with the process name, pid, and function name, and the // - generic "parameter" parameter. The resulting output will basically follow this scheme: // "pid","proces_name","function_name","FAILED/SUCCESS(0/1)","return_value","number_of_arguments","argument1->value","argument2->value"... // - Uses the "mutex" mutex to avoid concurrency when using the FltSendMessage() function. ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NTSTATUS sendLogs(ULONG pid, PWCHAR message, PWCHAR parameter) { NTSTATUS status = STATUS_SUCCESS; CHAR buf[MAXSIZE]; UNICODE_STRING processName; ULONG sizeBuf; LARGE_INTEGER timeout; timeout.QuadPart = -((LONGLONG)0.5*10*1000*1000); if(message == NULL) return STATUS_INVALID_PARAMETER; #ifdef DEBUG DbgPrint("SendLogs\n"); #endif processName.Length = 0; processName.MaximumLength = NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR); processName.Buffer = ExAllocatePoolWithTag(NonPagedPool, processName.MaximumLength, PROCNAME_TAG); if(!processName.Buffer) { KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, &timeout); KeReleaseMutex(&mutex, FALSE); return STATUS_NO_MEMORY; } status = getProcNameByPID(pid, &processName); if(!NT_SUCCESS(status)) { KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, &timeout); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; } status = RtlStringCbPrintfA(buf, MAXSIZE, "%d,%wZ,%ws,%ws\n", pid, &processName, message, parameter); if(!NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, &timeout); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; } status = RtlStringCbLengthA(buf, MAXSIZE, &sizeBuf); if(!NT_SUCCESS(status)) { KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, &timeout); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; } KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); #ifdef DEBUG DbgPrint("\tmsg : %s\n", buf); #endif status = FltSendMessage(filter, &clientPort, buf, sizeBuf, NULL, 0, NULL); if(status == STATUS_TIMEOUT) DbgPrint("STATUS_TIMEOUT !!\n"); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); #ifdef DEBUG if(!NT_SUCCESS(status)) DbgPrint("return : 0x%08x\n", status); #endif DEBUG return status; }
FLT_POSTOP_CALLBACK_STATUS claimsmanPostOperation( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) /*++ Routine Description: This routine is the post-operation completion routine for this miniFilter. This is non-pageable because it may be called at DPC level. Arguments: Data - Pointer to the filter callbackData that is passed to us. FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance, its associated volume and file object. CompletionContext - The completion context set in the pre-operation routine. Flags - Denotes whether the completion is successful or is being drained. Return Value: The return value is the status of the operation. --*/ { UNREFERENCED_PARAMETER(Data); UNREFERENCED_PARAMETER(CompletionContext); UNREFERENCED_PARAMETER(Flags); NTSTATUS status; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; PUNICODE_STRING fileName = NULL; PTOKEN_USER pTokenUser = NULL; UNICODE_STRING sidString; LARGE_INTEGER Timeout; Timeout.QuadPart = (LONGLONG)1 * 10 * 1000 * 1000; // If there is no client registered, bail out immediately! // If the event is from kernel, bail out immediately! // If the event check for existence of file, bail out immediately! if ( (ClaimsmanData.ClientPort == NULL) || (Data->RequestorMode == KernelMode) || (Data->IoStatus.Information == FILE_DOES_NOT_EXIST) || (Data->IoStatus.Information == FILE_EXISTS) ) { return FLT_POSTOP_FINISHED_PROCESSING; } // We got a log record, if there is a file object, get its name. if (FltObjects->FileObject != NULL) { status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); } else { // Can't get a name when there's no file object status = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS(status)) { FltParseFileNameInformation(nameInfo); fileName = &nameInfo->Name; // Produces way too much logging //PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, // ("claimsman!claimsmanPostOperation: fileName=%wZ\n", fileName)); } else { // No point continuing because we obviously did not get a filename anyways return FLT_POSTOP_FINISHED_PROCESSING; } //The only IRP you can trust for user information is IRP_MJ_CREATE. Things //like write can be in arbitrary thread context, and even if the call works //you can get the wrong SID. status = SeQueryInformationToken(SeQuerySubjectContextToken(&(Data->Iopb->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext)), TokenUser, &pTokenUser); if (STATUS_SUCCESS == status && RtlValidSid(pTokenUser->User.Sid)) { // Interesting extension? if (ClaimsmanCheckExtension(&nameInfo->Extension)) { CLAIMSMAN_MESSAGE msg; status = RtlConvertSidToUnicodeString(&sidString, pTokenUser->User.Sid, TRUE); if (NT_SUCCESS(status)) { PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("claimsman!claimsmanPostOperation: SID=%wZ\n", &sidString)); } else { // No point continuing because we obviously did not get a valid SID FltReleaseFileNameInformation(nameInfo); ExFreePool(pTokenUser); return FLT_POSTOP_FINISHED_PROCESSING; } if (ClaimsmanCheckUserIgnore(&sidString)) { // Ignored user! Bail out! FltReleaseFileNameInformation(nameInfo); ExFreePool(pTokenUser); RtlFreeUnicodeString(&sidString); return FLT_POSTOP_FINISHED_PROCESSING; } LONGLONG size; LONGLONG modified; getSizeModified(FltObjects->Instance, fileName, &size, &modified); InitializeMessage(&msg, &sidString, fileName, FltObjects->FileObject->ReadAccess, FltObjects->FileObject->WriteAccess, FltObjects->FileObject->DeleteAccess, size, modified, Data->IoStatus.Status); // Ready, send the message! // But only if there's a client connected if (ClaimsmanData.ClientPort != NULL) { FltSendMessage(ClaimsmanData.Filter, &ClaimsmanData.ClientPort, &msg, sizeof(msg), NULL, 0, &Timeout ); PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("claimsman!claimsmanPostOperation: sent message=%d\n", status)); } else { PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("claimsman!claimsmanPostOperation: no client connected!")); } RtlFreeUnicodeString(&sidString); } } FltReleaseFileNameInformation(nameInfo); if (pTokenUser != NULL) { ExFreePool(pTokenUser); } return FLT_POSTOP_FINISHED_PROCESSING; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Description : // Generates a message using "pid", "message" and "parameter" and sends it back to userland throught // a filter communication port. // Parameters : // _in_opt_ ULONG pid : Process ID from which the logs are produced. // _in_opt_ PWCHAR message : Message (function name most of the time). // _in_opt_ PWCHAR parameter : Function args. // Return value : // NTSTATUS : FltSendMessage return value. // Process : // - Retrieves the process name from the pid and saves the whole data into an ANSI string. // - Generates an ANSI string with the message, with the process name, pid, and function name, and the // - generic "parameter" parameter. The resulting output will basically follow this scheme: // "pid","process_name","function_name","FAILED/SUCCESS/BLOCKED(0/1/2)","return_value","number_of_arguments","argument1->value","argument2->value"... // - Uses the "mutex" mutex to avoid concurrency when using the FltSendMessage() function. ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NTSTATUS sendLogs(ULONG pid, PWCHAR message, PWCHAR parameter) { NTSTATUS status = STATUS_SUCCESS; CHAR buf[MAXSIZE]; UNICODE_STRING processName; ULONG sizeBuf; if(message == NULL) return STATUS_INVALID_PARAMETER; processName.Length = 0; processName.MaximumLength = NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR); processName.Buffer = ExAllocatePoolWithTag(NonPagedPool, processName.MaximumLength, PROCNAME_TAG); if(!processName.Buffer) { DbgPrint("ProcessName.Buffer error\n"); KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, NULL); KeReleaseMutex(&mutex, FALSE); return STATUS_NO_MEMORY; } status = getProcNameByPID(pid, &processName); if(!NT_SUCCESS(status)) { DbgPrint("getProcNameByPID() error\n"); KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, NULL); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; } status = RtlStringCbPrintfA(buf, MAXSIZE, "%d,%wZ,%ws,%ws\n", pid, &processName, message, parameter); if(!NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { DbgPrint("RtlStringCbPrintfA() error\n"); KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, NULL); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; } status = RtlStringCbLengthA(buf, MAXSIZE, &sizeBuf); if(!NT_SUCCESS(status)) { DbgPrint("RtlStringCbLengthA() error\n"); KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, "0,error,error,error\n", 20, NULL, 0, NULL); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; } KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE, NULL); status = FltSendMessage(filter, &clientPort, buf, sizeBuf, NULL, 0, NULL); KeReleaseMutex(&mutex, FALSE); ExFreePool(processName.Buffer); return status; }
NTSTATUS SendScanOrder( _In_ PFLT_FILTER FilterHandle, PUNICODE_STRING FilePath , _Out_ SCAN_RESULT * AnswerMessage) { NTSTATUS ntStatus = STATUS_SUCCESS; SCAN_RESULT replyMessage = NONE; PMESSAGE_CONTEXT scanMessage = NULL; LARGE_INTEGER liTimeOut; LARGE_INTEGER timeOut = {0}; ANSI_STRING AnsiString; ULONG replyLength =0; LONGLONG _1ms = 10000; LONGLONG localScanTimeout = 1000; RtlZeroMemory(&AnsiString, sizeof(ANSI_STRING)); // Check parameters. if (FilterHandle == NULL) { DbgPrint("[-] Error :: ArmaditoGuard!SendScanOrder :: Invalid filter handle parameter !! \n"); return STATUS_INVALID_PARAMETER; } if (gClientComPort == NULL) { DbgPrint("[-] Error :: ArmaditoGuard!SendScanOrder :: Scan service not connected yet !! \n"); return STATUS_INVALID_PARAMETER; } if (FilePath == NULL) { DbgPrint("[-] Error :: ArmaditoGuard!SendScanOrder :: Invalid FilePath parameter !! \n"); return STATUS_INVALID_PARAMETER; } if (AnswerMessage == NULL) { DbgPrint("[-] Error :: ArmaditoGuard!SendScanOrder :: Invalid AnswerMessage parameter !! \n"); return STATUS_INVALID_PARAMETER; } __try { // Allocate memory and initialize the scan struct. scanMessage = (PMESSAGE_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(MESSAGE_CONTEXT), MESSAGE_CONTEXT_BUF); if (scanMessage == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; __leave; } RtlZeroMemory(scanMessage, sizeof(MESSAGE_CONTEXT)); // Convert unicode string to ansi string for ring 3 process. ntStatus = RtlUnicodeStringToAnsiString(&AnsiString, (PCUNICODE_STRING)FilePath, TRUE); if(!NT_SUCCESS(ntStatus)){ DbgPrint("[-] Error :: ArmaditoGuard!SendScanOrder :: RtlUnicodeStringToAnsiString() routine failed !! \n"); __leave; } // Fill the SCAN_MESSAGE_CONTEXT struct. RtlCopyMemory(&(scanMessage->FileName), AnsiString.Buffer, AnsiString.Length); scanMessage->FileNameLength = AnsiString.Length; scanMessage->scan_result = NONE; // Set timeout (1second). liTimeOut.QuadPart = -100000000LL; //liTimeOut.QuadPart = -50000000LL; // 0.1 sec //timeOut = &liTimeOut; timeOut.QuadPart = -(localScanTimeout * _1ms); //replyLength = sizeof(FILTER_REPLY_HEADER) + sizeof(SCAN_RESULT); replyLength = sizeof(SCAN_RESULT); // Send message to the scan process throught the communication port. ntStatus = FltSendMessage(FilterHandle, &gClientComPort, (PVOID)scanMessage, sizeof(MESSAGE_CONTEXT), (PVOID)&replyMessage, &replyLength, &timeOut); if (!NT_SUCCESS(ntStatus)) { DbgPrint("[-] Error :: ArmaditoGuard!SendScanOrder :: FltSendMessage() routine failed !! \n"); __leave; } if(ntStatus == STATUS_TIMEOUT){ //DbgPrint("[-] Warning :: ArmaditoGuard!SendScanOrder :: FltSendMessage() returned with timeout status !! \n"); *AnswerMessage = TIMEOUT; } else { *AnswerMessage = replyMessage; //DbgPrint("[+] Debug :: ArmaditoGuard!SendScanOrder :: FltSendMessage() executed successfully !! :: Scan Result = [TODO] \n"); if (replyMessage != NONE) { ;//DbgPrint("[+] DEBUG :: ArmaditoGuard!SendScanOrder :: Reply message received successfully from the scan service :: Scan Result = [DBG_FLAG] \n"); } else { DbgPrint("[+] Warning :: ArmaditoGuard!SendScanOrder :: Reply message not received completly...\n"); } } } __finally { // // Whatever happens, this buffer is not required anymore after this routine. // if(scanMessage != NULL){ ExFreePoolWithTag(scanMessage, MESSAGE_CONTEXT_BUF); scanMessage = NULL; } // // Free ansi string if this one has been allocated. // if(AnsiString.Buffer != NULL){ RtlFreeAnsiString(&AnsiString); } } return ntStatus; }