using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace MalwareMultiScan.Backends.Backends.Abstracts { public abstract class AbstractLocalProcessScanBackend : AbstractScanBackend { private readonly ILogger _logger; protected AbstractLocalProcessScanBackend(ILogger logger) { _logger = logger; } protected abstract Regex MatchRegex { get; } protected abstract string BackendPath { get; } protected virtual bool ParseStdErr { get; } = false; protected virtual bool ThrowOnNonZeroExitCode { get; } = true; protected abstract string GetBackendArguments(string path); public override async Task ScanAsync(string path, CancellationToken cancellationToken) { var process = new Process { StartInfo = new ProcessStartInfo(BackendPath, GetBackendArguments(path)) { RedirectStandardError = true, RedirectStandardOutput = true, WorkingDirectory = Path.GetDirectoryName(BackendPath) ?? Directory.GetCurrentDirectory() } }; _logger.LogInformation( $"Starting process {process.StartInfo.FileName} " + $"with arguments {process.StartInfo.Arguments} " + $"in working directory {process.StartInfo.WorkingDirectory}"); process.Start(); cancellationToken.Register(() => { if (!process.HasExited) process.Kill(true); }); process.WaitForExit(); _logger.LogInformation($"Process has exited with code {process.ExitCode}"); var standardOutput = await process.StandardOutput.ReadToEndAsync(); var standardError = await process.StandardError.ReadToEndAsync(); _logger.LogDebug($"Process standard output: {standardOutput}"); _logger.LogDebug($"Process standard error: {standardError}"); if (ThrowOnNonZeroExitCode && process.ExitCode != 0) throw new ApplicationException($"Process has terminated with an exit code {process.ExitCode}"); return MatchRegex .Matches(ParseStdErr ? standardError : standardOutput) .Where(x => x.Success) .Select(x => x.Groups["threat"].Value) .ToArray(); } } }