Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
PsUtils.cs563 linesDownload Raw Back to utils
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Diagnostics;7using System.IO;8using System.Linq;9using System.Management.Automation.Language;10using System.Net.NetworkInformation;11using System.Runtime.CompilerServices;12using System.Runtime.InteropServices;13using System.Text;14using System.Xml;15 16namespace System.Management.Automation17{18    /// <summary>19    /// Defines generic utilities and helper methods for PowerShell.20    /// </summary>21    internal static class PsUtils22    {23        // Cache of the current process' parentId24        private static int? s_currentParentProcessId;25        private static readonly int s_currentProcessId = Environment.ProcessId;26 27        /// <summary>28        /// Retrieve the parent process of a process.29        ///30        /// Previously this code used WMI, but WMI is causing a CPU spike whenever the query gets called as it results in31        /// tzres.dll and tzres.mui.dll being loaded into every process to convert the time information to local format.32        /// For perf reasons, we resort to P/Invoke.33        /// </summary>34        /// <param name="current">The process we want to find the35        /// parent of</param>36        internal static Process GetParentProcess(Process current)37        {38            var processId = current.Id;39 40            // This is a common query (parent id for the current process)41            // Use cached value if available42            var parentProcessId = processId == s_currentProcessId && s_currentParentProcessId.HasValue ?43                 s_currentParentProcessId.Value :44                 Microsoft.PowerShell.ProcessCodeMethods.GetParentPid(current);45 46            // cache the current process parent pid if it hasn't been done yet47            if (processId == s_currentProcessId && !s_currentParentProcessId.HasValue)48            {49                s_currentParentProcessId = parentProcessId;50            }51 52            if (parentProcessId == 0)53                return null;54 55            try56            {57                Process returnProcess = Process.GetProcessById(parentProcessId);58 59                // Ensure the process started before the current60                // process, as it could have gone away and had the61                // PID recycled.62                if (returnProcess.StartTime <= current.StartTime)63                    return returnProcess;64                else65                    return null;66            }67            catch (ArgumentException)68            {69                // GetProcessById throws an ArgumentException when70                // you reach the top of the chain -- Explorer.exe71                // has a parent process, but you cannot retrieve it.72                return null;73            }74        }75 76        /// <summary>77        /// Return true/false to indicate whether the process architecture is ARM.78        /// </summary>79        /// <returns></returns>80        internal static bool IsRunningOnProcessArchitectureARM()81        {82            Architecture arch = RuntimeInformation.ProcessArchitecture;83            return arch == Architecture.Arm || arch == Architecture.Arm64;84        }85 86        internal static string GetHostName()87        {88            IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();89 90            string hostname = ipProperties.HostName;91            string domainName = ipProperties.DomainName;92 93            // CoreFX on Unix calls GLibc getdomainname()94            // which returns "(none)" if a domain name is not set by setdomainname()95            if (!string.IsNullOrEmpty(domainName) && !domainName.Equals("(none)", StringComparison.Ordinal))96            {97                hostname = hostname + "." + domainName;98            }99 100            return hostname;101        }102 103        internal static uint GetNativeThreadId()104        {105#if UNIX106            return Platform.NonWindowsGetThreadId();107#else108            return Interop.Windows.GetCurrentThreadId();109#endif110        }111 112        #region ASTUtils113 114        /// <summary>115        /// This method is to get the unique key for a UsingExpressionAst. The key is a base64116        /// encoded string based on the text of the UsingExpressionAst.117        ///118        /// This method is used when handling a script block that contains $using for Invoke-Command.119        ///120        /// When run Invoke-Command targeting a machine that runs PSv3 or above, we pass a dictionary121        /// to the remote end that contains the key of each UsingExpressionAst and its value. This method122        /// is used to generate the key.123        /// </summary>124        /// <param name="usingAst">A using expression.</param>125        /// <returns>Base64 encoded string as the key of the UsingExpressionAst.</returns>126        internal static string GetUsingExpressionKey(Language.UsingExpressionAst usingAst)127        {128            Diagnostics.Assert(usingAst != null, "Caller makes sure the parameter is not null");129 130            // We cannot call ToLowerInvariant unconditionally, because usingAst might131            // contain IndexExpressionAst in its SubExpression, such as132            //   $using:bar["AAAA"]133            // and the index "AAAA" might not get us the same value as "aaaa".134            //135            // But we do want a unique key to represent the same UsingExpressionAst's as much136            // as possible, so as to avoid sending redundant key-value's to remote machine.137            // As a workaround, we call ToLowerInvariant when the SubExpression of usingAst138            // is a VariableExpressionAst, because:139            //   (1) Variable name is case insensitive;140            //   (2) People use $using to refer to a variable most of the time.141            string usingAstText = usingAst.ToString();142            if (usingAst.SubExpression is Language.VariableExpressionAst)143            {144                usingAstText = usingAstText.ToLowerInvariant();145            }146 147            return StringToBase64Converter.StringToBase64String(usingAstText);148        }149 150        #endregion ASTUtils151 152        #region EvaluatePowerShellDataFile153 154        /// <summary>155        /// Evaluate a powershell data file as if it's a module manifest.156        /// </summary>157        /// <param name="parameterName"></param>158        /// <param name="psDataFilePath"></param>159        /// <param name="context"></param>160        /// <param name="skipPathValidation"></param>161        /// <returns></returns>162        internal static Hashtable EvaluatePowerShellDataFileAsModuleManifest(163                                     string parameterName,164                                     string psDataFilePath,165                                     ExecutionContext context,166                                     bool skipPathValidation)167        {168            // Use the same capabilities as the module manifest169            // e.g. allow 'PSScriptRoot' variable170            return EvaluatePowerShellDataFile(171                      parameterName,172                      psDataFilePath,173                      context,174                      Microsoft.PowerShell.Commands.ModuleCmdletBase.PermittedCmdlets,175                      new[] { "PSScriptRoot" },176                      allowEnvironmentVariables: true,177                      skipPathValidation: skipPathValidation);178        }179 180        /// <summary>181        /// Get a Hashtable object out of a PowerShell data file (.psd1)182        /// </summary>183        /// <param name="parameterName">184        /// Name of the parameter that takes the specified .psd1 file as a value185        /// </param>186        /// <param name="psDataFilePath">187        /// Path to the powershell data file188        /// </param>189        /// <param name="context">190        /// ExecutionContext to use191        /// </param>192        /// <param name="allowedCommands">193        /// Set of command names that are allowed to use in the .psd1 file194        /// </param>195        /// <param name="allowedVariables">196        /// Set of variable names that are allowed to use in the .psd1 file197        /// </param>198        /// <param name="allowEnvironmentVariables">199        /// If true, allow to use environment variables in the .psd1 file200        /// </param>201        /// <param name="skipPathValidation">202        /// If true, caller guarantees the path is valid203        /// </param>204        /// <returns></returns>205        internal static Hashtable EvaluatePowerShellDataFile(206                                     string parameterName,207                                     string psDataFilePath,208                                     ExecutionContext context,209                                     IEnumerable<string> allowedCommands,210                                     IEnumerable<string> allowedVariables,211                                     bool allowEnvironmentVariables,212                                     bool skipPathValidation)213        {214            if (!skipPathValidation && string.IsNullOrEmpty(parameterName))215            {216                throw PSTraceSource.NewArgumentNullException(nameof(parameterName));217            }218 219            if (string.IsNullOrEmpty(psDataFilePath))220            {221                throw PSTraceSource.NewArgumentNullException(nameof(psDataFilePath));222            }223 224            if (context == null)225            {226                throw PSTraceSource.NewArgumentNullException(nameof(context));227            }228 229            string resolvedPath;230            if (skipPathValidation)231            {232                resolvedPath = psDataFilePath;233            }234            else235            {236                #region "ValidatePowerShellDataFilePath"237 238                bool isPathValid = true;239 240                // File extension needs to be .psd1241                string pathExt = Path.GetExtension(psDataFilePath);242                if (string.IsNullOrEmpty(pathExt) ||243                    !StringLiterals.PowerShellDataFileExtension.Equals(pathExt, StringComparison.OrdinalIgnoreCase))244                {245                    isPathValid = false;246                }247 248                ProviderInfo provider;249                var resolvedPaths = context.SessionState.Path.GetResolvedProviderPathFromPSPath(psDataFilePath, out provider);250 251                // ConfigPath should be resolved as FileSystem provider252                if (provider == null || !Microsoft.PowerShell.Commands.FileSystemProvider.ProviderName.Equals(provider.Name, StringComparison.OrdinalIgnoreCase))253                {254                    isPathValid = false;255                }256 257                // ConfigPath should be resolved to a single path258                if (resolvedPaths.Count != 1)259                {260                    isPathValid = false;261                }262 263                if (!isPathValid)264                {265                    throw PSTraceSource.NewArgumentException(266                             parameterName,267                             ParserStrings.CannotResolvePowerShellDataFilePath,268                             psDataFilePath);269                }270 271                resolvedPath = resolvedPaths[0];272 273                #endregion "ValidatePowerShellDataFilePath"274            }275 276            #region "LoadAndEvaluatePowerShellDataFile"277 278            object evaluationResult;279            try280            {281                // Create the scriptInfo for the .psd1 file282                string dataFileName = Path.GetFileName(resolvedPath);283                var dataFileScriptInfo = new ExternalScriptInfo(dataFileName, resolvedPath, context);284                ScriptBlock scriptBlock = dataFileScriptInfo.ScriptBlock;285 286                // Validate the scriptblock287                scriptBlock.CheckRestrictedLanguage(allowedCommands, allowedVariables, allowEnvironmentVariables);288 289                // Evaluate the scriptblock290                object oldPsScriptRoot = context.GetVariableValue(SpecialVariables.PSScriptRootVarPath);291                try292                {293                    // Set the $PSScriptRoot before the evaluation294                    context.SetVariable(SpecialVariables.PSScriptRootVarPath, Path.GetDirectoryName(resolvedPath));295                    evaluationResult = PSObject.Base(scriptBlock.InvokeReturnAsIs());296                }297                finally298                {299                    context.SetVariable(SpecialVariables.PSScriptRootVarPath, oldPsScriptRoot);300                }301            }302            catch (RuntimeException ex)303            {304                throw PSTraceSource.NewInvalidOperationException(305                         ex,306                         ParserStrings.CannotLoadPowerShellDataFile,307                         psDataFilePath,308                         ex.Message);309            }310 311            if (evaluationResult is not Hashtable retResult)312            {313                throw PSTraceSource.NewInvalidOperationException(314                         ParserStrings.InvalidPowerShellDataFile,315                         psDataFilePath);316            }317 318            #endregion "LoadAndEvaluatePowerShellDataFile"319 320            return retResult;321        }322 323        #endregion EvaluatePowerShellDataFile324 325        internal static readonly string[] ManifestModuleVersionPropertyName = new[] { "ModuleVersion" };326        internal static readonly string[] ManifestGuidPropertyName = new[] { "GUID" };327        internal static readonly string[] ManifestPrivateDataPropertyName = new[] { "PrivateData" };328 329        internal static readonly string[] FastModuleManifestAnalysisPropertyNames = new[]330        {331            "AliasesToExport",332            "CmdletsToExport",333            "CompatiblePSEditions",334            "FunctionsToExport",335            "NestedModules",336            "RootModule",337            "ModuleToProcess",338            "ModuleVersion"339        };340 341        internal static Hashtable GetModuleManifestProperties(string psDataFilePath, string[] keys)342        {343            string dataFileContents = File.ReadAllText(psDataFilePath, Encoding.Default);344            ParseError[] parseErrors;345            var ast = (new Parser()).Parse(psDataFilePath, dataFileContents, null, out parseErrors, ParseMode.ModuleAnalysis);346            if (parseErrors.Length > 0)347            {348                var pe = new ParseException(parseErrors);349                throw PSTraceSource.NewInvalidOperationException(350                    pe,351                    ParserStrings.CannotLoadPowerShellDataFile,352                    psDataFilePath,353                    pe.Message);354            }355 356            var pipeline = ast.GetSimplePipeline(false, out _, out _);357            if (pipeline?.GetPureExpression() is HashtableAst hashtableAst)358            {359                var result = new Hashtable(StringComparer.OrdinalIgnoreCase);360                foreach (var pair in hashtableAst.KeyValuePairs)361                {362                    if (pair.Item1 is StringConstantExpressionAst key && keys.Contains(key.Value, StringComparer.OrdinalIgnoreCase))363                    {364                        try365                        {366                            var val = pair.Item2.SafeGetValue();367                            result[key.Value] = val;368                        }369                        catch370                        {371                            throw PSTraceSource.NewInvalidOperationException(372                                        ParserStrings.InvalidPowerShellDataFile,373                                        psDataFilePath);374                        }375                    }376                }377 378                return result;379            }380 381            throw PSTraceSource.NewInvalidOperationException(382                     ParserStrings.InvalidPowerShellDataFile,383                     psDataFilePath);384        }385    }386 387    /// <summary>388    /// This class provides helper methods for converting to/fro from389    /// string to base64string.390    /// </summary>391    internal static class StringToBase64Converter392    {393        /// <summary>394        /// Converts string to base64 encoded string.395        /// </summary>396        /// <param name="input">String to encode.</param>397        /// <returns>Base64 encoded string.</returns>398        internal static string StringToBase64String(string input)399        {400            // NTRAID#Windows Out Of Band Releases-926471-2005/12/27-JonN401            // shell crashes if you pass an empty script block to a native command402            if (input == null)403            {404                throw PSTraceSource.NewArgumentNullException(nameof(input));405            }406 407            string base64 = Convert.ToBase64String408                            (409                                Encoding.Unicode.GetBytes(input.ToCharArray())410                            );411            return base64;412        }413 414        /// <summary>415        /// Decodes base64 encoded string.416        /// </summary>417        /// <param name="base64">Base64 string to decode.</param>418        /// <returns>Decoded string.</returns>419        internal static string Base64ToString(string base64)420        {421            if (string.IsNullOrEmpty(base64))422            {423                throw PSTraceSource.NewArgumentNullException(nameof(base64));424            }425 426            string output = new string(Encoding.Unicode.GetChars(Convert.FromBase64String(base64)));427            return output;428        }429 430        /// <summary>431        /// Decodes base64 encoded string in to args array.432        /// </summary>433        /// <param name="base64"></param>434        /// <returns></returns>435        internal static object[] Base64ToArgsConverter(string base64)436        {437            if (string.IsNullOrEmpty(base64))438            {439                throw PSTraceSource.NewArgumentNullException(nameof(base64));440            }441 442            string decoded = new string(Encoding.Unicode.GetChars(Convert.FromBase64String(base64)));443 444            // Deserialize string445            XmlReader reader = XmlReader.Create(new StringReader(decoded), InternalDeserializer.XmlReaderSettingsForCliXml);446            object dso;447            Deserializer deserializer = new Deserializer(reader);448            dso = deserializer.Deserialize();449            if (!deserializer.Done())450            {451                // This helper function should move to host and it should provide appropriate452                // error message there.453                throw PSTraceSource.NewArgumentException(MinishellParameterBinderController.ArgsParameter);454            }455 456            if (dso is not PSObject mo)457            {458                // This helper function should move the host. Provide appropriate error message.459                // Format of args parameter is not correct.460                throw PSTraceSource.NewArgumentException(MinishellParameterBinderController.ArgsParameter);461            }462 463            if (mo.BaseObject is not ArrayList argsList)464            {465                // This helper function should move the host. Provide appropriate error message.466                // Format of args parameter is not correct.467                throw PSTraceSource.NewArgumentException(MinishellParameterBinderController.ArgsParameter);468            }469 470            return argsList.ToArray();471        }472    }473 474    /// <summary>475    /// A simple implementation of CRC32.476    /// See "CRC-32 algorithm" in https://en.wikipedia.org/wiki/Cyclic_redundancy_check.477    /// </summary>478    internal static class CRC32Hash479    {480        // CRC-32C polynomial representations481        private const uint polynomial = 0x1EDC6F41;482 483        private static readonly uint[] table;484 485        static CRC32Hash()486        {487            uint temp = 0;488            table = new uint[256];489 490            for (int i = 0; i < table.Length; i++)491            {492                temp = (uint)i;493                for (int j = 0; j < 8; j++)494                {495                    if ((temp & 1) == 1)496                    {497                        temp = (temp >> 1) ^ polynomial;498                    }499                    else500                    {501                        temp >>= 1;502                    }503                }504 505                table[i] = temp;506            }507        }508 509        private static uint Compute(byte[] buffer)510        {511            uint crc = 0xFFFFFFFF;512            for (int i = 0; i < buffer.Length; ++i)513            {514                var index = (byte)(crc ^ buffer[i] & 0xff);515                crc = (crc >> 8) ^ table[index];516            }517 518            return ~crc;519        }520 521        internal static byte[] ComputeHash(byte[] buffer)522        {523            uint crcResult = Compute(buffer);524            return BitConverter.GetBytes(crcResult);525        }526 527        internal static string ComputeHash(string input)528        {529            byte[] hashBytes = ComputeHash(Encoding.UTF8.GetBytes(input));530            return Convert.ToHexString(hashBytes);531        }532    }533 534    #region ReferenceEqualityComparer535 536    /// <summary>537    /// Equality comparer based on Object Identity.538    /// </summary>539    internal class ReferenceEqualityComparer : IEqualityComparer540    {541        bool IEqualityComparer.Equals(object x, object y)542        {543            return Object.ReferenceEquals(x, y);544        }545 546        int IEqualityComparer.GetHashCode(object obj)547        {548            // The Object.GetHashCode and RuntimeHelpers.GetHashCode methods are used in the following scenarios:549            //550            // Object.GetHashCode is useful in scenarios where you care about object value. Two strings with identical551            // contents will return the same value for Object.GetHashCode.552            //553            // RuntimeHelpers.GetHashCode is useful in scenarios where you care about object identity. Two strings with554            // identical contents will return different values for RuntimeHelpers.GetHashCode, because they are different555            // string objects, although their contents are the same.556 557            return RuntimeHelpers.GetHashCode(obj);558        }559    }560 561    #endregion562}563