Windows.System.VAD

This artifact enables enumeration of process memory sections via the Virtual Address Descriptor (VAD). The VAD is used by the Windows memory manager to describe allocated process memory ranges.

Available filters include process, mapping path, memory permissions or by content with yara.

Use the UploadSection switch to upload any sections.

A notebook suggestion is available for Strings analysis on uploaded sections.

NOTE:

  • ProtectionChoice is a choice to filter on section protection. Default is all sections and ProtectionRegex can override selection.
  • To filter on unmapped sections the MappingNameRegex: ^$ can be used.
  • When uploading sections during analysis, its recommended to run once for scoping, then a second time once confirmed for upload.

name: Windows.System.VAD
author: "Matt Green - @mgreen27"
description: |
  This artifact enables enumeration of process memory sections via the Virtual
  Address Descriptor (VAD). The VAD is used by the Windows memory manager to
  describe allocated process memory ranges.

  Available filters include process, mapping path, memory permissions
  or by content with yara.
  
  Use the UploadSection switch to upload any sections.
  
  A notebook suggestion is available for Strings analysis on uploaded sections.

  NOTE:

  - ProtectionChoice is a choice to filter on section protection. Default is
  all sections and ProtectionRegex can override selection.
  - To filter on unmapped sections the MappingNameRegex: ^$ can be used.
  - When uploading sections during analysis, its recommended to run once for 
  scoping, then a second time once confirmed for upload.

parameters:
  - name: ProcessRegex
    description: A regex applied to process names.
    default: .
    type: regex
  - name: PidRegex
    default: .
    type: regex
  - name: ProtectionChoice
    type: choices
    description: Select memory permission you would like to return. Default All.
    default: Any
    choices:
      - Any
      - Execute, read and write
      - Any executable
  - name: ProtectionRegex
    type: regex
    description: Allows a manual regex selection of section Protection permissions. If configured take preference over Protection choice.
  - name: MappingNameRegex
    type: regex
  - name: UploadSection
    description: Upload suspicious section.
    type: bool
  - name: SuspiciousContent
    description: A yara rule of suspicious section content
    type: yara
  - name: ContextBytes
    description: Include this amount of bytes around yara hit as context.
    default: 0
    type: int


sources:
  - query: |
      -- firstly find processes in scope
      LET processes = SELECT int(int=Pid) AS Pid,
              Name, Exe, CommandLine, StartTime
        FROM process_tracker_pslist()
        WHERE Name =~ ProcessRegex
            AND format(format="%d", args=Pid) =~ PidRegex
            AND log(message="Scanning pid %v : %v", args=[Pid, Name])

      -- next find sections in scope
      LET sections = SELECT * FROM foreach(
          row=processes,
          query={
            SELECT StartTime as ProcessCreateTime,Pid, Name, MappingName,
                format(format='%x-%x', args=[Address, Address+Size]) AS AddressRange,
                Address as _Address,
                State,Type,ProtectionMsg,Protection,
                Size as SectionSize,
                pathspec(
                    DelegateAccessor="process",
                    DelegatePath=Pid,
                    Path=Address) AS _PathSpec
            FROM vad(pid=Pid)
            WHERE if(condition=MappingNameRegex,
                    then= MappingName=~MappingNameRegex,
                    else= True)
                AND if(condition = ProtectionRegex,
                    then= Protection=~ProtectionRegex,
                    else= if(condition= ProtectionChoice='Any',
                        then= TRUE,
                    else= if(condition= ProtectionChoice='Execute, read and write',
                        then= Protection= 'xrw',
                    else= if(condition= ProtectionChoice='Any executable',
                        then= Protection=~'x'))))
          })

      -- if suspicious yara added, search for it
      LET yara_sections = SELECT *
        FROM foreach(row={
                SELECT * FROM sections
                WHERE NOT State =~ "RESERVE"
            }, query={
                SELECT
                    ProcessCreateTime, Pid, Name,MappingName,
                    AddressRange,State,Type,ProtectionMsg,
                    Protection,SectionSize,
                    dict(Rule=Rule,
                         Meta=Meta,
                         Tags=Tags,
                         Offset=String.Offset,
                         Name=String.Name) as YaraHit,
                    upload( accessor='scope',
                            file='String.Data',
                            name=format(format="%v-%v_%v.bin-%v-%v",
                            args=[
                                Name, Pid, AddressRange,
                                if(condition= String.Offset - ContextBytes < 0,
                                    then= 0,
                                    else= String.Offset - ContextBytes),
                                if(condition= String.Offset + ContextBytes > SectionSize,
                                    then= SectionSize,
                                    else= String.Offset + ContextBytes ) ])
                            ) as HitContext,
                    _PathSpec, _Address
                FROM yara(  blocksize=if(condition= SectionSize < 10000000,
                                            then= SectionSize,
                                            else= 10000000 ),
                            accessor='offset',
                            files=_PathSpec,
                            rules=SuspiciousContent,
                            end=SectionSize,  key='X',
                            number=1,
                            context=ContextBytes
                        )
            })

      -- finalise results
      LET results = SELECT *,
             process_tracker_callchain(id=Pid).Data as ProcessChain
        FROM if(condition= SuspiciousContent,
                    then= yara_sections,
                    else= sections)

      -- upload sections if selected
      LET upload_results = SELECT *,
        upload(accessor='sparse',
               file=pathspec(
                    DelegateAccessor="process",
                    DelegatePath=Pid,
                    Path=[dict(Offset=_Address, Length=SectionSize),]),
                name=pathspec(
                    Path=format(
                        format='%v-%v_%v.bin',
                        args= [ Name, Pid, AddressRange ]))) as SectionDump
            FROM results

      -- output rows
      SELECT * FROM if(condition= UploadSection,
                    then= upload_results,
                    else= results)

    notebook:
      - type: vql_suggestion
        name: Strings analysis
        template: |

            /*
            # Strings analysis
            */
            
            LET MinStringSize = 8
            LET FindPrintable = '''
                    rule find_strings {
                        strings:
                            $wide = /(^|[^ -~\s]\x00)([ -~\s]\x00){%#%,}(\x00|[^ -~\s]|$)/
                            $ascii = /(^|[^ -~\s])([ -~\s]{%#%,})([^ -~\s]|$)/
                        condition:
                            any of them
                    }'''
            LET YaraRule = regex_replace(source=FindPrintable,re='''\%\#\%''',replace=str(str=MinStringSize))
            
            
            LET sections = SELECT vfs_path, client_path,file_size, uploaded_size
                FROM uploads(client_id=ClientId, flow_id=FlowId)
                WHERE vfs_path =~ '\.bin$'
            
            LET find_result(name) = SELECT *
                FROM source(artifact="Windows.System.VAD")
                WHERE SectionDump.StoredName = name
                LIMIT 1
            
            
            LET row_results = SELECT *, find_result(name=client_path)[0] as Result
            FROM sections
            WHERE Result
            
            SELECT * FROM foreach(row=row_results,
                query={
                    SELECT
                        regex_replace(source=String.Data,re='[^ -~]',replace='') as String,
                        strip(prefix='$',string=String.Name) as Type,
                        String.Offset as Offset,
                        Result.MappingName as MappingName,
                        Result.AddressRange as AddressRange,
                        Result.Name as ProcesName, 
                        Result.Pid as Pid,
                        Result.Protection as Protection
                        --,vfs_path
                    FROM yara(
                            accessor='fs',
                            files=vfs_path,
                            rules=YaraRule,
                            key='X',
                            number=9999999999999999 )
                })
                WHERE NOT String =~ '''^\s*$'''

column_types:
  - name: HitContext
    type: preview_upload