Windows.System.Powershell.ModuleAnalysisCache

ModuleAnalysisCache stores metadata about loaded Powershell modules.

Recent updates include filters by regex to enable targeted hunting use cases.


name: Windows.System.Powershell.ModuleAnalysisCache
description: |
    ModuleAnalysisCache stores metadata about loaded Powershell modules.

    Recent updates include filters by regex to enable targeted hunting
    use cases.

reference:
 - https://github.com/PowerShell/PowerShell/blob/281b437a65360ae869d40f3766a1f2bbba786e5e/src/System.Management.Automation/engine/Modules/AnalysisCache.cs#L649

parameters:
  - name: GlobLookup
    default: C:\{Users\*,Windows\System32\config\systemprofile}\AppData\Local\Microsoft\Windows\PowerShell\ModuleAnalysisCache
  - name: ModulePathRegex
    description: Regex of installed ModulePath to target.
    default: .
    type: regex
  - name: ModulePathIgnoreRegex
    description: Regex of installed ModulePath to ignore.
    type: regex
  - name: FunctionNameRegex
    description: Regex of FunctionName to include.
    default: .
    type: regex

sources:
  - query: |
      LET Profile = '
       [
         ["Header", 0, [
           ["Signature", 0, "String", {"length": 13}],
           ["CountOfEntries", 14, "uint32"],
           ["Entries", 18, "Array",
                 {"type": "Entry", "count": "x => x.CountOfEntries"}]
         ]],

         ["Entry", "x=>x.Func.SizeOf + x.ModuleLength + 20", [
           ["Offset", 0, "Value", {"value": "x => x.StartOf"}],
           ["TimestampTicks", 0, "uint64"],
           ["ModuleLength", 8, "uint32"],
           ["ModuleName", 12, "String", {"length": "x => x.ModuleLength"}],
           ["CommandCount", "x => x.ModuleLength + 12", "uint32"],
           ["Func", "x => x.ModuleLength + 16", "Array",
                  {"type": "FunctionInfo", "count": "x => x.CommandCount"}],
           ["CountOfTypes", "x => x.Func.EndOf", "uint32"]
         ]],

         ["FunctionInfo", "x => x.NameLen + 8", [
           ["NameLen", 0, "uint32"],
           ["Name", 4, "String", {"length": "x => x.NameLen"}],
           ["Count", "x => x.NameLen + 4", "uint32"]
         ]]
       ]
      '
      LET parsed = SELECT OSPath,
         parse_binary(filename=OSPath, profile=Profile, struct="Header") AS Header
      FROM glob(globs=GlobLookup)

      SELECT * FROM foreach(row=parsed,
      query={
         SELECT * FROM foreach(row=Header.Entries,
         query={
            SELECT OSPath, ModuleName,
                  timestamp(epoch=TimestampTicks/10000000 - 62136892800) AS Timestamp,
                  Func.Name AS Functions
            FROM scope()
            WHERE ModuleName =~ ModulePathRegex
                AND NOT if(condition= ModulePathIgnoreRegex,
                            then= ModuleName =~ ModulePathIgnoreRegex,
                            else= False )
                AND filter(list=Functions,regex=FunctionNameRegex)
         })
      })