├── .github └── workflows │ └── terraform.yml ├── LICENSE ├── README.md ├── images └── flow.png └── terraform ├── Lambda_all └── lambda_function_all.py ├── Lambda_plugin └── lambda_function_plugin.py ├── analysis_script ├── analysis.py ├── template.css ├── template.html └── template.js ├── api.tf ├── batch.tf ├── cloudwatch.tf ├── codecommit.tf ├── docker ├── Dockerfile └── run.sh ├── ecr.tf ├── iam.tf ├── init.tf ├── lambda.tf ├── network.tf ├── s3.tf ├── s3 └── error.html ├── sns.tf └── variables.tf /.github/workflows/terraform.yml: -------------------------------------------------------------------------------- 1 | name: 'Deploy to AWS using Terraform' 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'terraform/**' 9 | 10 | jobs: 11 | 12 | setup_terraform: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Configure AWS credentials 21 | uses: aws-actions/configure-aws-credentials@v1 22 | with: 23 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 24 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 25 | aws-region: ${{ secrets.AWS_REGION }} 26 | 27 | - name: Login to ECR 28 | uses: docker/login-action@v2 29 | with: 30 | registry: ${{ secrets.AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com 31 | username: ${{ secrets.AWS_ACCESS_KEY_ID }} 32 | password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 33 | 34 | - name: Caching state 35 | id: cache 36 | uses: actions/cache@v3 37 | with: 38 | path: ~/terraform/.terraform 39 | key: ${{ runner.os }}-terraform-state 40 | 41 | - name: Setup Terraform 42 | uses: hashicorp/setup-terraform@v2 43 | with: 44 | terraform_version: 1.2.3 45 | 46 | - name: Terraform Init 47 | run: terraform init 48 | working-directory: ./terraform 49 | 50 | - name: Terraform Plan 51 | run: terraform plan -no-color 52 | working-directory: ./terraform 53 | 54 | - name: Terraform Apply 55 | run: terraform apply -auto-approve -no-color 56 | working-directory: ./terraform 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The 3-Clause BSD License 2 | 3 | SPDX short identifier: BSD-3-Clause 4 | Note: This license has also been called the "New BSD License" or "Modified BSD License". See also the 2-clause BSD License. 5 | 6 | --- 7 | 8 | Copyright 2023 JPCERT Coordination Center 9 | 10 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither JPCERT Coordination Center nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory Forensic on Cloud 2 | 3 | This repository is a PoC for memory forensic on AWS. 4 | 5 | ![flow](images/flow.png) 6 | 7 | ## Support OS 8 | 9 | This system only supports memory forensics on Windows OS. 10 | 11 | ## Requirements Service 12 | 13 | Get an account for the following services. 14 | 15 | * AWS 16 | * GitHub 17 | 18 | ## How to Use 19 | 20 | ### Clone or Fork This Repository 21 | 22 | ```bash 23 | $ git clone https://github.com/JPCERTCC/MemoryForensic-on-Cloud.git 24 | ``` 25 | 26 | ### Setup Configuration 27 | 28 | #### variables.tf 29 | 30 | ```python 31 | # Region to build the system 32 | variable "region" { 33 | default = "us-east-1" 34 | } 35 | 36 | # Docker image name 37 | variable "image_name" { 38 | default = "memory-analysis" 39 | } 40 | 41 | # Dockerfile directory 42 | variable "docker_dir" { 43 | default = "docker" 44 | } 45 | 46 | # Analysis script directory 47 | variable "codecommit_dir" { 48 | default = "analysis_script" 49 | } 50 | 51 | # Username to commit to Codecommit 52 | variable "codecommit_username" { 53 | default = "terraformer" 54 | } 55 | 56 | # E-mail to commit to Codecommit 57 | variable "codecommit_email" { 58 | default = "test@example.com" 59 | } 60 | 61 | # System name to build on AWS 62 | variable "app_name" { 63 | default = "memory-analysis" 64 | } 65 | 66 | # IP address to access API Gateway and S3 67 | variable "trusted_ip" { 68 | default = "192.168.1.1/32" 69 | } 70 | 71 | # Network address used by VPC 72 | variable "cidr_block_vpc" { 73 | default = "172.20.0.0/16" 74 | } 75 | 76 | # AWS access key 77 | variable "aws_access_key_id" { 78 | default = "" 79 | } 80 | 81 | # AWS secret access key 82 | variable "aws_secret_access_key" { 83 | default = "" 84 | } 85 | 86 | # E-mail to notify the analysis status 87 | variable "sns_email" { 88 | default = "" 89 | } 90 | 91 | 92 | ``` 93 | 94 | ### Setup GitHub Actions 95 | 96 | Set GitHub Actions secrets. 97 | 98 | * AWS_ACCESS_KEY_ID 99 | * AWS_SECRET_ACCESS_KEY 100 | * AWS_ACCOUNT_NUMBER 101 | * AWS_REGION 102 | 103 | How to use GitHub Actions secrets. https://docs.github.com/en/actions/security-guides/encrypted-secrets 104 | 105 | Finally, commit the repository to GitHub. 106 | -------------------------------------------------------------------------------- /images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JPCERTCC/MemoryForensic-on-Cloud/1ec5699389b5c1c060e032bb168993b8a1ecc379/images/flow.png -------------------------------------------------------------------------------- /terraform/Lambda_all/lambda_function_all.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | import logging 4 | import datetime 5 | 6 | JOB_QUEUE = os.environ['JOB_QUEUE'] 7 | JOB_DEFINITION = os.environ['JOB_DEFINITION'] 8 | 9 | logger = logging.getLogger() 10 | logLevelTable={'DEBUG':logging.DEBUG,'INFO':logging.INFO,'WARNING':logging.WARNING,'ERROR':logging.ERROR,'CRITICAL':logging.CRITICAL} 11 | if 'logging_level' in os.environ and os.environ['logging_level'] in logLevelTable : 12 | logLevel=logLevelTable[os.environ['logging_level']] 13 | else: 14 | logLevel=logging.WARNING 15 | logger.setLevel(logLevel) 16 | 17 | def lambda_handler(event, context): 18 | file_name = event['detail']['object']['key'] 19 | source_ip = event['detail']['source-ip-address'] 20 | logger.info(" : upload file name {0} from {1}".format(file_name, source_ip)) 21 | commands = ["/tmp/run.sh", file_name, "all"] 22 | 23 | logger.info(" : commands {0}".format(commands)) 24 | 25 | client = boto3.client('batch') 26 | 27 | response = client.submit_job( 28 | jobName = "MemForensicJob_" + file_name, 29 | jobQueue = JOB_QUEUE, 30 | jobDefinition = JOB_DEFINITION, 31 | containerOverrides={ 32 | 'command': commands 33 | } 34 | ) 35 | print(response) 36 | -------------------------------------------------------------------------------- /terraform/Lambda_plugin/lambda_function_plugin.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import boto3 4 | import logging 5 | import datetime 6 | 7 | JOB_QUEUE = os.environ['JOB_QUEUE'] 8 | JOB_DEFINITION = os.environ['JOB_DEFINITION'] 9 | 10 | # Sha256 Check 11 | HASH_CHECK = r"[%*+=\[\]\\/|;:\"<>?,&]" 12 | 13 | logger = logging.getLogger() 14 | logLevelTable={'DEBUG':logging.DEBUG,'INFO':logging.INFO,'WARNING':logging.WARNING,'ERROR':logging.ERROR,'CRITICAL':logging.CRITICAL} 15 | if 'logging_level' in os.environ and os.environ['logging_level'] in logLevelTable : 16 | logLevel=logLevelTable[os.environ['logging_level']] 17 | else: 18 | logLevel=logging.WARNING 19 | logger.setLevel(logLevel) 20 | 21 | def lambda_handler(event, context): 22 | file_name = event['queryStringParameters']['hash'] 23 | plugin = event['queryStringParameters']['plugin'] 24 | if not re.search(r"\A[0-9a-zA-Z]{64}\Z", file_name): 25 | logger.error(" : Hash value does not match format.") 26 | return "Error" 27 | 28 | if not re.search(r"\A[a-z_]{4,25}\Z", plugin): 29 | logger.error(" : Plugin name does not match format.") 30 | return "Error" 31 | 32 | if plugin in "dumpfiles": 33 | try: 34 | pid = event['queryStringParameters']['pid'] 35 | if not re.search(r"\A[0-9]{1,6}\Z", pid): 36 | logger.error(" : PID does not match format.") 37 | return "Error" 38 | logger.info(" : dump file {0} pid {1}".format(file_name, pid)) 39 | commands = ["/tmp/run.sh", file_name, "dumpfiles", "-p", pid] 40 | except: 41 | logger.error(" : Can't get pid option.") 42 | return "Error" 43 | else: 44 | logger.info(" : Analysis file {0} plugin {1}".format(file_name, plugin)) 45 | commands = ["/tmp/run.sh", file_name, plugin] 46 | 47 | logger.info(" : commands {0}".format(commands)) 48 | 49 | client = boto3.client('batch') 50 | 51 | response = client.submit_job( 52 | jobName = "MemForensicJob_" + plugin + "_" + file_name, 53 | jobQueue = JOB_QUEUE, 54 | jobDefinition = JOB_DEFINITION, 55 | containerOverrides={ 56 | 'command': commands 57 | } 58 | ) 59 | 60 | logger.debug(response) 61 | 62 | if plugin in "dumpfiles": 63 | return { 64 | 'statusCode':303, 65 | 'headers':{ 66 | 'Location': "http://" + os.environ['S3_BUCKET'] + "/" + file_name + "/" + pid + ".zip", 67 | } 68 | } 69 | else: 70 | return "Start job." 71 | -------------------------------------------------------------------------------- /terraform/analysis_script/analysis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import git 7 | import json 8 | import time 9 | import yara 10 | import shutil 11 | import hashlib 12 | import datetime 13 | import argparse 14 | import subprocess 15 | 16 | 17 | FPATH = os.path.dirname(os.path.abspath(__file__)) 18 | MAIN_DIR = os.path.join(FPATH, 'main') 19 | YARA_DIR = os.path.join(FPATH, "yara_sig") 20 | YARA_FILE = os.path.join(FPATH, "all.yara") 21 | 22 | with open(os.path.join(FPATH, "template.html"), "r") as f: 23 | HTML_DATA = f.read() 24 | 25 | with open(os.path.join(FPATH, "template.js"), "r") as f: 26 | JS_DATA = f.read() 27 | 28 | with open(os.path.join(FPATH, "template.css"), "r") as f: 29 | CSS_DATA = f.read() 30 | 31 | yara_repos = ["https://github.com/JPCERTCC/jpcert-yara.git", 32 | #"https://github.com/Neo23x0/signature-base.git", 33 | "https://github.com/volexity/threat-intel.git", 34 | "https://github.com/mandiant/red_team_tool_countermeasures.git", 35 | ] 36 | 37 | memory_backet_name = os.environ['IMAGE_BACKET_NAME'] 38 | result_backet_name = os.environ['BACKET_NAME'] 39 | repo_name = os.environ['REPO_NAME'] 40 | region = os.environ['REGION'] 41 | api_gateway_url = os.environ['API_GATEWAY_URL'] 42 | 43 | parser = argparse.ArgumentParser(description="Analysis memory image using Volatility3.") 44 | parser.add_argument(dest="file", action="store", type=str, metavar="FILE", 45 | help="Memory dump file.") 46 | parser.add_argument(dest="plugin", action="store", type=str, metavar="PLUGIN_NAME", 47 | help="Set using plugin name. (if 'all', execute all plugins)") 48 | parser.add_argument("-p", "--pid", dest="pid", action="store", type=str, metavar="PID", 49 | help="Set the pid of dumpfile..") 50 | args = parser.parse_args() 51 | 52 | PLUGINS = ["windows.bigpools.BigPools", 53 | "windows.cachedump.Cachedump", 54 | "windows.callbacks.Callbacks", 55 | "windows.cmdline.CmdLine", 56 | "windows.driverirp.DriverIrp", 57 | "windows.driverscan.DriverScan", 58 | "windows.envars.Envars", 59 | "windows.filescan.FileScan", 60 | "windows.getservicesids.GetServiceSIDs", 61 | "windows.getsids.GetSIDs", 62 | #"windows.handles.Handles", # slow analysis 63 | "windows.hashdump.Hashdump", 64 | "windows.lsadump.Lsadump", 65 | #"windows.memmap.Memmap", # slow analysis 66 | "windows.modscan.ModScan", 67 | "windows.mutantscan.MutantScan", 68 | "windows.netscan.NetScan", 69 | "windows.netstat.NetStat", 70 | "windows.poolscanner.PoolScanner", 71 | "windows.privileges.Privs", 72 | "windows.pstree.PsTree", 73 | "windows.registry.certificates.Certificates", # bug? 74 | "windows.registry.hivelist.HiveList", 75 | "windows.registry.hivescan.HiveScan", 76 | #"windows.registry.userassist.UserAssist", # Set HIVE offset (--offset) from HiveScan 77 | "windows.skeleton_key_check.Skeleton_Key_Check", 78 | "windows.ssdt.SSDT", 79 | #"windows.strings.Strings", # Set pid (--pid) 80 | "windows.svcscan.SvcScan", 81 | "windows.symlinkscan.SymlinkScan", 82 | "windows.vadinfo.VadInfo", 83 | "windows.verinfo.VerInfo", 84 | "windows.virtmap.VirtMap",] 85 | 86 | DUMP_PLUGINS = ["windows.pslist.PsList", 87 | "windows.psscan.PsScan", 88 | "windows.malfind.Malfind", 89 | "windows.modules.Modules", 90 | "windows.dlllist.DllList",] 91 | 92 | def download_from_s3(filename): 93 | print("[+]{0} : download memory image".format(datetime.datetime.now())) 94 | subprocess.call('aws s3 cp s3://' + memory_backet_name + '/' + filename + ' /tmp/' + repo_name + '/ ', shell=True) 95 | 96 | def yara_download(): 97 | if os.path.exists(YARA_DIR) is True: 98 | shutil.rmtree(YARA_DIR) 99 | 100 | if os.path.exists(YARA_FILE) is True: 101 | os.remove(YARA_FILE) 102 | 103 | for i, yara_repo in enumerate(yara_repos): 104 | for j in range(1, 4): 105 | try: 106 | git.Repo.clone_from(yara_repo, os.path.join(YARA_DIR, str(i))) 107 | print("[+]{0} : download yara rule {1}".format(datetime.datetime.now(), yara_repo)) 108 | break 109 | except git.GitCommandError as ex: 110 | print("[!]{0} :error:{1}: retry:{2}/3".format(datetime.datetime.now(), ex, j)) 111 | time.sleep(10 * j) 112 | print("[+]{0} :retrying".format(datetime.datetime.now())) 113 | if j == 3: 114 | sys.exit("[!]{0} : Can't clone {1}".format(datetime.datetime.now(), yara_repo)) 115 | 116 | with open(YARA_FILE, "a", encoding="utf-8") as f: 117 | for cur_dir, dirs, files in os.walk(YARA_DIR): 118 | for file in files: 119 | if (file.endswith(".yara") or file.endswith(".yar")) and not file.startswith("all-yara.yar"): 120 | try: 121 | yara.compile(os.path.abspath(os.path.join(cur_dir, file))) 122 | with open(os.path.abspath(os.path.join(cur_dir, file)), "r") as fyara: 123 | yara_data = fyara.read() 124 | f.write(yara_data + "\n") 125 | except: 126 | print("[!]{0} : Can't load yara file {1}".format(datetime.datetime.now(), os.path.abspath(os.path.join(cur_dir, file)))) 127 | 128 | def get_hash(filename): 129 | hash = hashlib.sha256() 130 | 131 | with open(filename, 'rb') as f: 132 | while True: 133 | chunk = f.read(2048 * hash.block_size) 134 | if len(chunk) == 0: 135 | break 136 | 137 | hash.update(chunk) 138 | 139 | return hash.hexdigest() 140 | 141 | def vol_analysis_plugin(filename, archive_path, plugin): 142 | print("[+]{0} : running analysis {1}".format(datetime.datetime.now(), plugin)) 143 | subprocess.call("vol -r json -f " + filename + " --plugin-dirs /tmp/impfuzzy/impfuzzy_for_Volatility3 " + plugin + " > " + archive_path + "/" + plugin + ".json", shell=True) 144 | 145 | def vol_analysis_yara(filename, archive_path): 146 | print("[+]{0} : running analysis yarascan.YaraScan".format(datetime.datetime.now())) 147 | subprocess.call("vol -r json -f " + filename + " yarascan.YaraScan --yara-file " + YARA_FILE + " > " + archive_path + "/yarascan.YaraScan.json", shell=True) 148 | 149 | def vol_analysis_ps(filename, archive_path): 150 | for plugin in DUMP_PLUGINS: 151 | print("[+]{0} : running analysis {1}".format(datetime.datetime.now(), plugin)) 152 | subprocess.call("vol -c config.json -r json " + plugin + " --dump > " + archive_path + "/" + plugin + ".json", shell=True) 153 | subprocess.call("mv *.dmp " + archive_path, shell=True) 154 | 155 | def vol_analysis_timeline(filename, archive_path): 156 | print("[+]{0} : running analysis timeliner.Timeliner".format(datetime.datetime.now())) 157 | subprocess.call("vol -r json -f " + filename + " timeliner.Timeliner > " + archive_path + "/timeliner.Timeliner.json", shell=True) 158 | 159 | def vol_analysis_printkey(filename, archive_path): 160 | print("[+]{0} : running analysis windows.registry.printkey.PrintKey".format(datetime.datetime.now())) 161 | with open(os.path.join(archive_path, "windows.registry.hivelist.HiveList.json"), "r") as f: 162 | json_load = json.load(f) 163 | for data in json_load: 164 | offset = "0x{:x}".format(data["Offset"]) 165 | print("[+]{0} : running analysis windows.registry.printkey.PrintKey {1}".format(datetime.datetime.now(), offset)) 166 | subprocess.call("vol -c config.json -r json windows.registry.printkey.PrintKey --offset=" + offset + " --recurse > " + archive_path + "/windows.registry.printkey.PrintKey." + offset + ".json", shell=True) 167 | 168 | def vol_analysis(filename, archive_path): 169 | subprocess.call("vol --write-config -r json -f " + filename + " windows.info.Info > " + archive_path + "/windows.info.Info.json", shell=True) 170 | 171 | for plugin in PLUGINS: 172 | print("[+]{0} : running analysis {1}".format(datetime.datetime.now(), plugin)) 173 | subprocess.call("vol -c config.json -r json " + plugin + " > " + archive_path + "/" + plugin + ".json", shell=True) 174 | 175 | def vol_analysis_dumpfile(filename, archive_path, pid): 176 | print("[+]{0} : running analysis windows.dumpfiles.DumpFiles".format(datetime.datetime.now())) 177 | subprocess.call("vol -r json -f " + filename + " windows.dumpfiles.DumpFiles --pid " + pid, shell=True) 178 | subprocess.call("zip -e --password=infected " + archive_path + "/" + pid + ".zip file*", shell=True) 179 | 180 | def main(): 181 | download_from_s3(args.file) 182 | 183 | print("[+]{0} : Start analysis".format(datetime.datetime.now())) 184 | 185 | memfile = os.path.join(FPATH, args.file) 186 | if os.path.isfile(memfile): 187 | print("[+]{0} : Lode file {1}".format(datetime.datetime.now(), memfile)) 188 | else: 189 | sys.exit("[!]{0} : Can't load file {1}".format(datetime.datetime.now(), memfile)) 190 | 191 | id = get_hash(memfile) 192 | archive_path = os.path.join(MAIN_DIR, id) 193 | 194 | if os.path.exists(MAIN_DIR) is False: 195 | os.mkdir(MAIN_DIR) 196 | 197 | if os.path.exists(archive_path) is False: 198 | os.mkdir(archive_path) 199 | print("[+]{0} : Created directory {1}".format(datetime.datetime.now(), archive_path)) 200 | 201 | if args.plugin in "all": 202 | yara_download() 203 | vol_analysis(memfile, archive_path) 204 | vol_analysis_ps(memfile, archive_path) 205 | vol_analysis_timeline(memfile, archive_path) 206 | vol_analysis_yara(memfile, archive_path) 207 | vol_analysis_printkey(memfile, archive_path) 208 | if args.plugin in "dumpfiles": 209 | vol_analysis_dumpfile(memfile, archive_path, args.pid) 210 | if args.plugin in "impfuzzy": 211 | vol_analysis_plugin(memfile, archive_path, "pehash.ImpFuzzy") 212 | if args.plugin in "imphash": 213 | vol_analysis_plugin(memfile, archive_path, "pehash.ImpHash") 214 | 215 | print("[+]{0} : Finished analysis".format(datetime.datetime.now())) 216 | 217 | # create result page 218 | with open(os.path.join(archive_path, "index.html"), "w", encoding="utf-8") as f: 219 | f.write(HTML_DATA.format(**{"hash": args.file})) 220 | 221 | with open(os.path.join(archive_path, "script.js"), "w", encoding="utf-8") as f: 222 | f.write(JS_DATA.format(**{"api_gateway_url": api_gateway_url})) 223 | 224 | with open(os.path.join(archive_path, "tree-boxes.css"), "w", encoding="utf-8") as f: 225 | f.write(CSS_DATA) 226 | 227 | time.sleep(1) 228 | subprocess.call('aws s3 cp /tmp/' + repo_name + '/main s3://' + result_backet_name + '/ --exclude "*" --include "*.html" --include "*.json" --include "*.js" --include "*.zip" --include "*.css" --include "*.dmp" --recursive ', shell=True) 229 | print("[+]{0} : Uploaded result".format(datetime.datetime.now())) 230 | 231 | if __name__ == "__main__": 232 | main() 233 | -------------------------------------------------------------------------------- /terraform/analysis_script/template.css: -------------------------------------------------------------------------------- 1 | #tree-container { 2 | width: 100%; 3 | } 4 | 5 | .svgContainer { 6 | display: block; 7 | margin: auto; 8 | } 9 | 10 | .node { 11 | cursor: pointer; 12 | } 13 | 14 | .node-rect { 15 | } 16 | 17 | .node-rect-closed { 18 | stroke-width: 2px; 19 | stroke: rgb(0,0,0); 20 | } 21 | 22 | .link { 23 | fill: none; 24 | stroke: lightsteelblue; 25 | stroke-width: 2px; 26 | } 27 | 28 | .linkselected { 29 | fill: none; 30 | stroke: tomato; 31 | stroke-width: 2px; 32 | } 33 | 34 | .arrow { 35 | fill: lightsteelblue; 36 | stroke-width: 1px; 37 | } 38 | 39 | .arrowselected { 40 | fill: tomato; 41 | stroke-width: 2px; 42 | } 43 | 44 | .link text { 45 | font: 7px sans-serif; 46 | fill: #CC0000; 47 | } 48 | 49 | .wordwrap { 50 | white-space: pre-wrap; /* CSS3 */ 51 | white-space: -moz-pre-wrap; /* Firefox */ 52 | white-space: -pre-wrap; /* Opera <7 */ 53 | white-space: -o-pre-wrap; /* Opera 7 */ 54 | word-wrap: break-word; /* IE */ 55 | } 56 | 57 | .node-text { 58 | font: 7px sans-serif; 59 | color: white; 60 | } 61 | 62 | .tooltip-text-container { 63 | height: 100%; 64 | width: 100%; 65 | } 66 | 67 | .tooltip-text { 68 | visibility: hidden; 69 | font: 7px sans-serif; 70 | color: white; 71 | display: block; 72 | padding: 5px; 73 | } 74 | 75 | .tooltip-box { 76 | background: rgba(0, 0, 0, 0.7); 77 | visibility: hidden; 78 | position: absolute; 79 | border-style: solid; 80 | border-width: 1px; 81 | border-color: black; 82 | border-top-right-radius: 0.5em; 83 | } 84 | 85 | p { 86 | display: inline; 87 | } 88 | 89 | .textcolored { 90 | color: orange; 91 | } 92 | 93 | a.exchangeName { 94 | color: orange; 95 | } 96 | 97 | .b-example-divider { 98 | flex-shrink: 0; 99 | width: 1.5rem; 100 | height: 100vh; 101 | background-color: rgba(0, 0, 0, .1); 102 | border: solid rgba(0, 0, 0, .15); 103 | border-width: 1px 0; 104 | box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15); 105 | } 106 | 107 | .bi { 108 | vertical-align: -.125em; 109 | pointer-events: none; 110 | fill: currentColor; 111 | } 112 | 113 | .dropdown-toggle { outline: 0; } 114 | 115 | .nav-flush .nav-link { 116 | border-radius: 0; 117 | } 118 | 119 | .btn-toggle { 120 | display: inline-flex; 121 | align-items: center; 122 | padding: .25rem .5rem; 123 | font-weight: 600; 124 | color: rgba(0, 0, 0, .65); 125 | background-color: transparent; 126 | border: 0; 127 | } 128 | .btn-toggle:hover, 129 | .btn-toggle:focus { 130 | color: rgba(0, 0, 0, .85); 131 | background-color: #d2f4ea; 132 | } 133 | 134 | .btn-toggle::before { 135 | width: 1.25em; 136 | line-height: 0; 137 | content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,280,280,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); 138 | transition: transform .35s ease; 139 | transform-origin: .5em 50%; 140 | } 141 | 142 | .btn-toggle[aria-expanded="true"] { 143 | color: rgba(0, 0, 0, .85); 144 | } 145 | .btn-toggle[aria-expanded="true"]::before { 146 | transform: rotate(90deg); 147 | } 148 | 149 | .btn-toggle-nav a { 150 | display: inline-flex; 151 | padding: .1875rem .5rem; 152 | margin-top: .125rem; 153 | margin-left: 1.25rem; 154 | text-decoration: none; 155 | } 156 | .btn-toggle-nav a:hover, 157 | .btn-toggle-nav a:focus { 158 | background-color: #d2f4ea; 159 | } 160 | 161 | .scrollarea { 162 | overflow-y: auto; 163 | } 164 | 165 | .fw-semibold { font-weight: 600; } 166 | .lh-tight { line-height: 1.25; } 167 | -------------------------------------------------------------------------------- /terraform/analysis_script/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Analysis Results 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 32 |
33 |
34 |
35 | 36 | 37 | Analysis Menu 38 | 39 | 166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /terraform/analysis_script/template.js: -------------------------------------------------------------------------------- 1 | $.fn.dataTable.moment('YYYY-MM-DDThh:mm:ss'); 2 | 3 | function queryTimeliner() {{ 4 | html = '
Accessed DateChanged DateCreated DateDescriptionModified DatePlugin
'; 5 | document.getElementById("resultTable").innerHTML = html; 6 | 7 | $("#tree-container").empty(); 8 | 9 | var myTable = $("#onTable").dataTable({{ 10 | "pageLength": 50, 11 | ajax: {{ 12 | "url": "timeliner.Timeliner.json", 13 | "type": "GET", 14 | "dataSrc": "", 15 | }}, 16 | columns: [ 17 | {{data: 'Accessed Date'}}, 18 | {{data: 'Changed Date'}}, 19 | {{data: 'Created Date'}}, 20 | {{data: 'Description'}}, 21 | {{data: 'Modified Date'}}, 22 | {{data: 'Plugin'}}, 23 | ], 24 | }}); 25 | }} 26 | 27 | function queryBigPools() {{ 28 | html = '
AllocationNumberOfBytesPoolTypeTag
'; 29 | document.getElementById("resultTable").innerHTML = html; 30 | 31 | $("#tree-container").empty(); 32 | 33 | var myTable = $("#onTable").dataTable({{ 34 | "pageLength": 50, 35 | ajax: {{ 36 | "url": "windows.bigpools.BigPools.json", 37 | "type": "GET", 38 | "dataSrc": "", 39 | }}, 40 | columns: [ 41 | {{data: 'Allocation'}}, 42 | {{data: 'NumberOfBytes'}}, 43 | {{data: 'PoolType'}}, 44 | {{data: 'Tag'}}, 45 | ], 46 | }}); 47 | }} 48 | 49 | function queryCachedump() {{ 50 | html = '
UsernameDomainDomain nameHash
'; 51 | document.getElementById("resultTable").innerHTML = html; 52 | 53 | $("#tree-container").empty(); 54 | 55 | var myTable = $("#onTable").dataTable({{ 56 | "pageLength": 50, 57 | ajax: {{ 58 | "url": "windows.cachedump.Cachedump.json", 59 | "type": "GET", 60 | "dataSrc": "", 61 | }}, 62 | columns: [ 63 | {{data: 'Username'}}, 64 | {{data: 'Domain'}}, 65 | {{data: 'Domain name'}}, 66 | {{data: 'Hash'}}, 67 | ], 68 | }}); 69 | }} 70 | 71 | function queryCallbacks() {{ 72 | html = '
CallbackDetailModuleSymbolType
'; 73 | document.getElementById("resultTable").innerHTML = html; 74 | 75 | $("#tree-container").empty(); 76 | 77 | var myTable = $("#onTable").dataTable({{ 78 | "pageLength": 50, 79 | ajax: {{ 80 | "url": "windows.callbacks.Callbacks.json", 81 | "type": "GET", 82 | "dataSrc": "", 83 | }}, 84 | columns: [ 85 | {{data: 'Callback'}}, 86 | {{data: 'Detail'}}, 87 | {{data: 'Module'}}, 88 | {{data: 'Symbol'}}, 89 | {{data: 'Type'}}, 90 | ], 91 | }}); 92 | }} 93 | 94 | function queryCmdLine() {{ 95 | html = '
ArgsPIDProcess
'; 96 | document.getElementById("resultTable").innerHTML = html; 97 | 98 | $("#tree-container").empty(); 99 | 100 | var myTable = $("#onTable").dataTable({{ 101 | "pageLength": 50, 102 | ajax: {{ 103 | "url": "windows.cmdline.CmdLine.json", 104 | "type": "GET", 105 | "dataSrc": "", 106 | }}, 107 | columns: [ 108 | {{data: 'Args'}}, 109 | {{data: 'PID'}}, 110 | {{data: 'Process'}}, 111 | ], 112 | }}); 113 | }} 114 | 115 | function queryDllList() {{ 116 | html = '
BaseLoadTimeNamePIDPathProcessSize
'; 117 | document.getElementById("resultTable").innerHTML = html; 118 | 119 | $("#tree-container").empty(); 120 | 121 | var myTable = $("#onTable").dataTable({{ 122 | "pageLength": 50, 123 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 124 | $('td:eq(3)', nRow).html('' + aData["PID"] + ''); 125 | return nRow; 126 | }}, 127 | ajax: {{ 128 | "url": "windows.dlllist.DllList.json", 129 | "type": "GET", 130 | "dataSrc": "", 131 | }}, 132 | columns: [ 133 | {{data: 'Base'}}, 134 | {{data: 'LoadTime'}}, 135 | {{data: 'Name'}}, 136 | {{data: 'PID'}}, 137 | {{data: 'Path'}}, 138 | {{data: 'Process'}}, 139 | {{data: 'Size'}}, 140 | ], 141 | }}); 142 | }} 143 | 144 | function queryDriverIrp() {{ 145 | html = '
AddressDriver NameIRPModuleSymbol
'; 146 | document.getElementById("resultTable").innerHTML = html; 147 | 148 | $("#tree-container").empty(); 149 | 150 | var myTable = $("#onTable").dataTable({{ 151 | "pageLength": 50, 152 | ajax: {{ 153 | "url": "windows.driverirp.DriverIrp.json", 154 | "type": "GET", 155 | "dataSrc": "", 156 | }}, 157 | columns: [ 158 | {{data: 'Address'}}, 159 | {{data: 'Driver Name'}}, 160 | {{data: 'IRP'}}, 161 | {{data: 'Module'}}, 162 | {{data: 'Symbol'}}, 163 | ], 164 | }}); 165 | }} 166 | 167 | function queryDriverScan() {{ 168 | html = '
Driver NameNameService KeySizeStart
'; 169 | document.getElementById("resultTable").innerHTML = html; 170 | 171 | $("#tree-container").empty(); 172 | 173 | var myTable = $("#onTable").dataTable({{ 174 | "pageLength": 50, 175 | ajax: {{ 176 | "url": "windows.driverscan.DriverScan.json", 177 | "type": "GET", 178 | "dataSrc": "", 179 | }}, 180 | columns: [ 181 | {{data: 'Driver Name'}}, 182 | {{data: 'Name'}}, 183 | {{data: 'Service Key'}}, 184 | {{data: 'Size'}}, 185 | {{data: 'Start'}}, 186 | ], 187 | }}); 188 | }} 189 | 190 | function queryEnvars() {{ 191 | html = '
BlockPIDProcessValueVariable
'; 192 | document.getElementById("resultTable").innerHTML = html; 193 | 194 | $("#tree-container").empty(); 195 | 196 | var myTable = $("#onTable").dataTable({{ 197 | "pageLength": 50, 198 | ajax: {{ 199 | "url": "windows.envars.Envars.json", 200 | "type": "GET", 201 | "dataSrc": "", 202 | }}, 203 | columns: [ 204 | {{data: 'Block'}}, 205 | {{data: 'PID'}}, 206 | {{data: 'Process'}}, 207 | {{data: 'Value'}}, 208 | {{data: 'Variable'}}, 209 | ], 210 | }}); 211 | }} 212 | 213 | function queryFileScan() {{ 214 | html = '
NameOffsetSize
'; 215 | document.getElementById("resultTable").innerHTML = html; 216 | 217 | $("#tree-container").empty(); 218 | 219 | var myTable = $("#onTable").dataTable({{ 220 | "pageLength": 50, 221 | ajax: {{ 222 | "url": "windows.filescan.FileScan.json", 223 | "type": "GET", 224 | "dataSrc": "", 225 | }}, 226 | columns: [ 227 | {{data: 'Name'}}, 228 | {{data: 'Offset'}}, 229 | {{data: 'Size'}}, 230 | ], 231 | }}); 232 | }} 233 | 234 | function queryGetServiceSIDs() {{ 235 | html = '
SIDService
'; 236 | document.getElementById("resultTable").innerHTML = html; 237 | 238 | $("#tree-container").empty(); 239 | 240 | var myTable = $("#onTable").dataTable({{ 241 | "pageLength": 50, 242 | ajax: {{ 243 | "url": "windows.getservicesids.GetServiceSIDs.json", 244 | "type": "GET", 245 | "dataSrc": "", 246 | }}, 247 | columns: [ 248 | {{data: 'SID'}}, 249 | {{data: 'Service'}}, 250 | ], 251 | }}); 252 | }} 253 | 254 | function queryGetSIDs() {{ 255 | html = '
NamePIDProcessSID
'; 256 | document.getElementById("resultTable").innerHTML = html; 257 | 258 | $("#tree-container").empty(); 259 | 260 | var myTable = $("#onTable").dataTable({{ 261 | "pageLength": 50, 262 | ajax: {{ 263 | "url": "windows.getsids.GetSIDs.json", 264 | "type": "GET", 265 | "dataSrc": "", 266 | }}, 267 | columns: [ 268 | {{data: 'Name'}}, 269 | {{data: 'PID'}}, 270 | {{data: 'Process'}}, 271 | {{data: 'SID'}}, 272 | ], 273 | }}); 274 | }} 275 | 276 | function queryHashdump() {{ 277 | html = '
Userlmhashnthashrid
'; 278 | document.getElementById("resultTable").innerHTML = html; 279 | 280 | $("#tree-container").empty(); 281 | 282 | var myTable = $("#onTable").dataTable({{ 283 | "pageLength": 50, 284 | ajax: {{ 285 | "url": "windows.hashdump.Hashdump.json", 286 | "type": "GET", 287 | "dataSrc": "", 288 | }}, 289 | columns: [ 290 | {{data: 'User'}}, 291 | {{data: 'lmhash'}}, 292 | {{data: 'nthash'}}, 293 | {{data: 'rid'}}, 294 | ], 295 | }}); 296 | }} 297 | 298 | function queryLsadump() {{ 299 | html = '
HexKeySecret
'; 300 | document.getElementById("resultTable").innerHTML = html; 301 | 302 | $("#tree-container").empty(); 303 | 304 | var myTable = $("#onTable").dataTable({{ 305 | "pageLength": 50, 306 | ajax: {{ 307 | "url": "windows.lsadump.Lsadump.json", 308 | "type": "GET", 309 | "dataSrc": "", 310 | }}, 311 | columns: [ 312 | {{data: 'Hex'}}, 313 | {{data: 'Key'}}, 314 | {{data: 'Secret'}}, 315 | ], 316 | }}); 317 | }} 318 | 319 | function queryMalfind() {{ 320 | html = '
DisasmHexdumpPIDProcessTag
'; 321 | document.getElementById("resultTable").innerHTML = html; 322 | 323 | $("#tree-container").empty(); 324 | 325 | var myTable = $("#onTable").dataTable({{ 326 | "pageLength": 50, 327 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 328 | $('td:eq(2)', nRow).html('' + aData["PID"] + ''); 329 | return nRow; 330 | }}, 331 | ajax: {{ 332 | "url": "windows.malfind.Malfind.json", 333 | "type": "GET", 334 | "dataSrc": "", 335 | }}, 336 | columns: [ 337 | {{data: 'Disasm'}}, 338 | {{data: 'Hexdump'}}, 339 | {{data: 'PID'}}, 340 | {{data: 'Process'}}, 341 | {{data: 'Tag'}}, 342 | ], 343 | }}); 344 | }} 345 | 346 | function queryModScan() {{ 347 | html = '
BaseNamePathSize
'; 348 | document.getElementById("resultTable").innerHTML = html; 349 | 350 | $("#tree-container").empty(); 351 | 352 | var myTable = $("#onTable").dataTable({{ 353 | "pageLength": 50, 354 | ajax: {{ 355 | "url": "windows.modscan.ModScan.json", 356 | "type": "GET", 357 | "dataSrc": "", 358 | }}, 359 | columns: [ 360 | {{data: 'Base'}}, 361 | {{data: 'Name'}}, 362 | {{data: 'Path'}}, 363 | {{data: 'Size'}}, 364 | ], 365 | }}); 366 | }} 367 | 368 | function queryModules() {{ 369 | html = '
BaseNamePathSize
'; 370 | document.getElementById("resultTable").innerHTML = html; 371 | 372 | $("#tree-container").empty(); 373 | 374 | var myTable = $("#onTable").dataTable({{ 375 | "pageLength": 50, 376 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 377 | $('td:eq(1)', nRow).html('' + aData["Name"] + ''); 378 | return nRow; 379 | }}, 380 | ajax: {{ 381 | "url": "windows.modules.Modules.json", 382 | "type": "GET", 383 | "dataSrc": "", 384 | }}, 385 | columns: [ 386 | {{data: 'Base'}}, 387 | {{data: 'Name'}}, 388 | {{data: 'Path'}}, 389 | {{data: 'Size'}}, 390 | ], 391 | }}); 392 | }} 393 | 394 | function queryMutantScan() {{ 395 | html = '
NameOffset
'; 396 | document.getElementById("resultTable").innerHTML = html; 397 | 398 | $("#tree-container").empty(); 399 | 400 | var myTable = $("#onTable").dataTable({{ 401 | "pageLength": 50, 402 | ajax: {{ 403 | "url": "windows.mutantscan.MutantScan.json", 404 | "type": "GET", 405 | "dataSrc": "", 406 | }}, 407 | columns: [ 408 | {{data: 'Name'}}, 409 | {{data: 'Offset'}}, 410 | ], 411 | }}); 412 | }} 413 | 414 | function queryNetScan() {{ 415 | html = '
CreatedForeignAddrForeignPortLocalAddrLocalPortOwnerPIDProtoState
'; 416 | document.getElementById("resultTable").innerHTML = html; 417 | 418 | $("#tree-container").empty(); 419 | 420 | var myTable = $("#onTable").dataTable({{ 421 | "pageLength": 50, 422 | ajax: {{ 423 | "url": "windows.netscan.NetScan.json", 424 | "type": "GET", 425 | "dataSrc": "", 426 | }}, 427 | columns: [ 428 | {{data: 'Created'}}, 429 | {{data: 'ForeignAddr'}}, 430 | {{data: 'ForeignPort'}}, 431 | {{data: 'LocalAddr'}}, 432 | {{data: 'LocalPort'}}, 433 | {{data: 'Owner'}}, 434 | {{data: 'PID'}}, 435 | {{data: 'Proto'}}, 436 | {{data: 'State'}}, 437 | ], 438 | }}); 439 | }} 440 | 441 | function queryNetStat() {{ 442 | html = '
CreatedForeignAddrForeignPortLocalAddrLocalPortOwnerPIDProtoState
'; 443 | document.getElementById("resultTable").innerHTML = html; 444 | 445 | $("#tree-container").empty(); 446 | 447 | var myTable = $("#onTable").dataTable({{ 448 | "pageLength": 50, 449 | ajax: {{ 450 | "url": "windows.netstat.NetStat.json", 451 | "type": "GET", 452 | "dataSrc": "", 453 | }}, 454 | columns: [ 455 | {{data: 'Created'}}, 456 | {{data: 'ForeignAddr'}}, 457 | {{data: 'ForeignPort'}}, 458 | {{data: 'LocalAddr'}}, 459 | {{data: 'LocalPort'}}, 460 | {{data: 'Owner'}}, 461 | {{data: 'PID'}}, 462 | {{data: 'Proto'}}, 463 | {{data: 'State'}}, 464 | ], 465 | }}); 466 | }} 467 | 468 | function queryPoolScanner() {{ 469 | html = '
LayerNameTag
'; 470 | document.getElementById("resultTable").innerHTML = html; 471 | 472 | $("#tree-container").empty(); 473 | 474 | var myTable = $("#onTable").dataTable({{ 475 | "pageLength": 50, 476 | ajax: {{ 477 | "url": "windows.poolscanner.PoolScanner.json", 478 | "type": "GET", 479 | "dataSrc": "", 480 | }}, 481 | columns: [ 482 | {{data: 'Layer'}}, 483 | {{data: 'Name'}}, 484 | {{data: 'Tag'}}, 485 | ], 486 | }}); 487 | }} 488 | 489 | function queryPsList() {{ 490 | html = '
CreateTimeExitTimeHandlesImageFileNamePIDPPIDSessionIdThreadsWow64
'; 491 | document.getElementById("resultTable").innerHTML = html; 492 | 493 | $("#tree-container").empty(); 494 | 495 | var myTable = $("#onTable").dataTable({{ 496 | "pageLength": 50, 497 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 498 | $('td:eq(4)', nRow).html('' + aData["PID"] + ''); 499 | return nRow; 500 | }}, 501 | ajax: {{ 502 | "url": "windows.pslist.PsList.json", 503 | "type": "GET", 504 | "dataSrc": "", 505 | }}, 506 | columns: [ 507 | {{data: 'CreateTime'}}, 508 | {{data: 'ExitTime'}}, 509 | {{data: 'Handles'}}, 510 | {{data: 'ImageFileName'}}, 511 | {{data: 'PID'}}, 512 | {{data: 'PPID'}}, 513 | {{data: 'SessionId'}}, 514 | {{data: 'Threads'}}, 515 | {{data: 'Wow64'}}, 516 | ], 517 | }}); 518 | }} 519 | 520 | function queryPsScan() {{ 521 | html = '
CreateTimeExitTimeHandlesImageFileNamePIDPPIDSessionIdThreadsWow64
'; 522 | document.getElementById("resultTable").innerHTML = html; 523 | 524 | $("#tree-container").empty(); 525 | 526 | var myTable = $("#onTable").dataTable({{ 527 | "pageLength": 50, 528 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 529 | $('td:eq(4)', nRow).html('' + aData["PID"] + ''); 530 | return nRow; 531 | }}, 532 | ajax: {{ 533 | "url": "windows.psscan.PsScan.json", 534 | "type": "GET", 535 | "dataSrc": "", 536 | }}, 537 | columns: [ 538 | {{data: 'CreateTime'}}, 539 | {{data: 'ExitTime'}}, 540 | {{data: 'Handles'}}, 541 | {{data: 'ImageFileName'}}, 542 | {{data: 'PID'}}, 543 | {{data: 'PPID'}}, 544 | {{data: 'SessionId'}}, 545 | {{data: 'Threads'}}, 546 | {{data: 'Wow64'}}, 547 | ], 548 | }}); 549 | }} 550 | 551 | function queryPrivs() {{ 552 | html = '
AttributesDescriptionPIDPrivilegeProcessValue
'; 553 | document.getElementById("resultTable").innerHTML = html; 554 | 555 | $("#tree-container").empty(); 556 | 557 | var myTable = $("#onTable").dataTable({{ 558 | "pageLength": 50, 559 | ajax: {{ 560 | "url": "windows.privileges.Privs.json", 561 | "type": "GET", 562 | "dataSrc": "", 563 | }}, 564 | columns: [ 565 | {{data: 'Attributes'}}, 566 | {{data: 'Description'}}, 567 | {{data: 'PID'}}, 568 | {{data: 'Privilege'}}, 569 | {{data: 'Process'}}, 570 | {{data: 'Value'}}, 571 | ], 572 | }}); 573 | }} 574 | 575 | function queryCertificates() {{ 576 | html = '
Certificate pathCertificate sectionCertificate IDCertificate name
'; 577 | document.getElementById("resultTable").innerHTML = html; 578 | 579 | $("#tree-container").empty(); 580 | 581 | var myTable = $("#onTable").dataTable({{ 582 | "pageLength": 50, 583 | ajax: {{ 584 | "url": "windows.registry.certificates.Certificates.json", 585 | "type": "GET", 586 | "dataSrc": "", 587 | }}, 588 | columns: [ 589 | {{data: 'Certificate path'}}, 590 | {{data: 'Certificate section'}}, 591 | {{data: 'Certificate ID'}}, 592 | {{data: 'Certificate name'}}, 593 | ], 594 | }}); 595 | }} 596 | 597 | function queryHiveList() {{ 598 | html = '<
FileFullPathOffset
'; 599 | document.getElementById("resultTable").innerHTML = html; 600 | 601 | $("#tree-container").empty(); 602 | 603 | var myTable = $("#onTable").dataTable({{ 604 | "pageLength": 50, 605 | ajax: {{ 606 | "url": "windows.registry.hivelist.HiveList.json", 607 | "type": "GET", 608 | "dataSrc": "", 609 | }}, 610 | columns: [ 611 | {{data: 'FileFullPath'}}, 612 | {{data: 'Offset'}}, 613 | ], 614 | }}); 615 | }} 616 | 617 | function queryHiveScan() {{ 618 | html = '
Offset
'; 619 | document.getElementById("resultTable").innerHTML = html; 620 | 621 | $("#tree-container").empty(); 622 | 623 | var myTable = $("#onTable").dataTable({{ 624 | "pageLength": 50, 625 | ajax: {{ 626 | "url": "windows.registry.hivescan.HiveScan.json", 627 | "type": "GET", 628 | "dataSrc": "", 629 | }}, 630 | columns: [ 631 | {{data: 'Offset'}}, 632 | ], 633 | }}); 634 | }} 635 | 636 | function queryPrintKey() {{ 637 | html = '<
FileFullPathOffset
'; 638 | document.getElementById("resultTable").innerHTML = html; 639 | 640 | $("#tree-container").empty(); 641 | 642 | var myTable = $("#onTable").dataTable({{ 643 | "pageLength": 50, 644 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 645 | $('td:eq(1)', nRow).html('' + "0x" + aData["Offset"].toString(16) + ''); 646 | return nRow; 647 | }}, 648 | ajax: {{ 649 | "url": "windows.registry.hivelist.HiveList.json", 650 | "type": "GET", 651 | "dataSrc": "", 652 | }}, 653 | columns: [ 654 | {{data: 'FileFullPath'}}, 655 | {{data: 'Offset'}}, 656 | ], 657 | }}); 658 | }} 659 | 660 | function queryPrintKeySub(offset) {{ 661 | html = '
Last Write TimeTypeKeyNameData
'; 662 | document.getElementById("resultTable").innerHTML = ""; 663 | document.getElementById("resultTable").innerHTML = html; 664 | 665 | var myTable = $("#onTable").dataTable({{ 666 | "pageLength": 50, 667 | ajax: {{ 668 | "url": "windows.registry.printkey.PrintKey." + offset + ".json", 669 | "type": "GET", 670 | "dataSrc": function(obj) {{ 671 | return flat(obj); 672 | }} 673 | }}, 674 | columns: [ 675 | {{data: 'Last Write Time'}}, 676 | {{data: 'Type'}}, 677 | {{data: 'Key'}}, 678 | {{data: 'Name'}}, 679 | {{data: 'Data'}}, 680 | ], 681 | }}); 682 | }} 683 | 684 | function flat(array) {{ 685 | var result = []; 686 | array.forEach(function (a) {{ 687 | result.push(a); 688 | if (Array.isArray(a.__children)) {{ 689 | result = result.concat(flat(a.__children)); 690 | }} 691 | }}); 692 | return result; 693 | }} 694 | 695 | function querySkeleton_Key_Check() {{ 696 | html = '
PIDProcessSkeleton Key Foundrc4HmacDecryptrc4HmacInitialize
'; 697 | document.getElementById("resultTable").innerHTML = html; 698 | 699 | $("#tree-container").empty(); 700 | 701 | var myTable = $("#onTable").dataTable({{ 702 | "pageLength": 50, 703 | ajax: {{ 704 | "url": "windows.skeleton_key_check.Skeleton_Key_Check.json", 705 | "type": "GET", 706 | "dataSrc": "", 707 | }}, 708 | columns: [ 709 | {{data: 'PID'}}, 710 | {{data: 'Process'}}, 711 | {{data: 'Skeleton Key Found'}}, 712 | {{data: 'rc4HmacDecrypt'}}, 713 | {{data: 'rc4HmacInitialize'}}, 714 | ], 715 | }}); 716 | }} 717 | 718 | function querySSDT() {{ 719 | html = '
AddressIndexModuleSymbol
'; 720 | document.getElementById("resultTable").innerHTML = html; 721 | 722 | $("#tree-container").empty(); 723 | 724 | var myTable = $("#onTable").dataTable({{ 725 | "pageLength": 50, 726 | ajax: {{ 727 | "url": "windows.ssdt.SSDT.json", 728 | "type": "GET", 729 | "dataSrc": "", 730 | }}, 731 | columns: [ 732 | {{data: 'Address'}}, 733 | {{data: 'Index'}}, 734 | {{data: 'Module'}}, 735 | {{data: 'Symbol'}}, 736 | ], 737 | }}); 738 | }} 739 | 740 | function querySvcScan() {{ 741 | html = '
BinaryDisplayNameOrderPIDStartStateType
'; 742 | document.getElementById("resultTable").innerHTML = html; 743 | 744 | $("#tree-container").empty(); 745 | 746 | var myTable = $("#onTable").dataTable({{ 747 | "pageLength": 50, 748 | ajax: {{ 749 | "url": "windows.svcscan.SvcScan.json", 750 | "type": "GET", 751 | "dataSrc": "", 752 | }}, 753 | columns: [ 754 | {{data: 'Binary'}}, 755 | {{data: 'Display'}}, 756 | {{data: 'Name'}}, 757 | {{data: 'Order'}}, 758 | {{data: 'PID'}}, 759 | {{data: 'Start'}}, 760 | {{data: 'State'}}, 761 | {{data: 'Type'}}, 762 | ], 763 | }}); 764 | }} 765 | 766 | function querySymlinkScan() {{ 767 | html = '
CreateTimeFrom NameTo Name
'; 768 | document.getElementById("resultTable").innerHTML = html; 769 | 770 | $("#tree-container").empty(); 771 | 772 | var myTable = $("#onTable").dataTable({{ 773 | "pageLength": 50, 774 | ajax: {{ 775 | "url": "windows.symlinkscan.SymlinkScan.json", 776 | "type": "GET", 777 | "dataSrc": "", 778 | }}, 779 | columns: [ 780 | {{data: 'CreateTime'}}, 781 | {{data: 'From Name'}}, 782 | {{data: 'To Name'}}, 783 | ], 784 | }}); 785 | }} 786 | 787 | function queryVadInfo() {{ 788 | html = '
CommitChargeEnd VPNFilePIDParentPrivateMemoryProcessProtectionStart VPNTag
'; 789 | document.getElementById("resultTable").innerHTML = html; 790 | 791 | $("#tree-container").empty(); 792 | 793 | var myTable = $("#onTable").dataTable({{ 794 | "pageLength": 50, 795 | ajax: {{ 796 | "url": "windows.vadinfo.VadInfo.json", 797 | "type": "GET", 798 | "dataSrc": "", 799 | }}, 800 | columns: [ 801 | {{data: 'CommitCharge'}}, 802 | {{data: 'End VPN'}}, 803 | {{data: 'File'}}, 804 | {{data: 'PID'}}, 805 | {{data: 'Parent'}}, 806 | {{data: 'PrivateMemory'}}, 807 | {{data: 'Process'}}, 808 | {{data: 'Protection'}}, 809 | {{data: 'Start VPN'}}, 810 | {{data: 'Tag'}}, 811 | ], 812 | }}); 813 | }} 814 | 815 | function queryVerInfo() {{ 816 | html = '
BaseBuildMajorMinorNamePIDProcessProduct
'; 817 | document.getElementById("resultTable").innerHTML = html; 818 | 819 | $("#tree-container").empty(); 820 | 821 | var myTable = $("#onTable").dataTable({{ 822 | "pageLength": 50, 823 | ajax: {{ 824 | "url": "windows.verinfo.VerInfo.json", 825 | "type": "GET", 826 | "dataSrc": "", 827 | }}, 828 | columns: [ 829 | {{data: 'Base'}}, 830 | {{data: 'Build'}}, 831 | {{data: 'Major'}}, 832 | {{data: 'Minor'}}, 833 | {{data: 'Name'}}, 834 | {{data: 'PID'}}, 835 | {{data: 'Process'}}, 836 | {{data: 'Product'}}, 837 | ], 838 | }}); 839 | }} 840 | 841 | function queryVirtMap() {{ 842 | html = '
Start offsetEnd offsetRegion
'; 843 | document.getElementById("resultTable").innerHTML = html; 844 | 845 | $("#tree-container").empty(); 846 | 847 | var myTable = $("#onTable").dataTable({{ 848 | "pageLength": 50, 849 | ajax: {{ 850 | "url": "windows.virtmap.VirtMap.json", 851 | "type": "GET", 852 | "dataSrc": "", 853 | }}, 854 | columns: [ 855 | {{data: 'Start offset'}}, 856 | {{data: 'End offset'}}, 857 | {{data: 'Region'}}, 858 | ], 859 | }}); 860 | }} 861 | 862 | function queryYaraScan() {{ 863 | html = '
OffsetPIDRuleComponentValue
'; 864 | document.getElementById("resultTable").innerHTML = html; 865 | 866 | $("#tree-container").empty(); 867 | 868 | var myTable = $("#onTable").dataTable({{ 869 | "pageLength": 50, 870 | ajax: {{ 871 | "url": "yarascan.YaraScan.json", 872 | "type": "GET", 873 | "dataSrc": "", 874 | }}, 875 | columns: [ 876 | {{data: 'Offset'}}, 877 | {{data: 'PID'}}, 878 | {{data: 'Rule'}}, 879 | {{data: 'Component'}}, 880 | {{data: 'Value'}}, 881 | ], 882 | }}); 883 | }} 884 | 885 | function queryImpHash(hash) {{ 886 | html = '
ImageFileNameModule BaseModule NamePIDimphash
'; 887 | document.getElementById("resultTable").innerHTML = html; 888 | 889 | $("#tree-container").empty(); 890 | $.fn.dataTable.ext.errMode = 'none'; 891 | 892 | var myTable = $("#onTable").dataTable({{ 893 | "pageLength": 50, 894 | ajax: {{ 895 | "url": "pehash.ImpHash.json", 896 | "type": "GET", 897 | "dataSrc": "", 898 | "error": function(jqXHR, textStatus, errorThrown){{ 899 | runAPI(hash, "imphash"); 900 | document.getElementById("resultTable").innerHTML = ""; 901 | document.getElementById("resultTable").innerHTML = '
Now processing...
'; 902 | }} 903 | }}, 904 | columns: [ 905 | {{data: 'ImageFileName'}}, 906 | {{data: 'Module Base'}}, 907 | {{data: 'Module Name'}}, 908 | {{data: 'PID'}}, 909 | {{data: 'imphash'}}, 910 | ], 911 | }}); 912 | }} 913 | 914 | function queryImpFuzzy(hash) {{ 915 | html = '
ImageFileNameModule BaseModule NamePIDimpfuzzy
'; 916 | document.getElementById("resultTable").innerHTML = html; 917 | 918 | $("#tree-container").empty(); 919 | 920 | var myTable = $("#onTable").dataTable({{ 921 | "pageLength": 50, 922 | ajax: {{ 923 | "url": "pehash.ImpFuzzy.json", 924 | "type": "GET", 925 | "dataSrc": "", 926 | "error": function(jqXHR, textStatus, errorThrown){{ 927 | runAPI(hash, "impfuzzy"); 928 | document.getElementById("resultTable").innerHTML = ""; 929 | document.getElementById("resultTable").innerHTML = '
Now processing...
'; 930 | }} 931 | }}, 932 | columns: [ 933 | {{data: 'ImageFileName'}}, 934 | {{data: 'Module Base'}}, 935 | {{data: 'Module Name'}}, 936 | {{data: 'PID'}}, 937 | {{data: 'impfuzzy'}}, 938 | ], 939 | }}); 940 | }} 941 | 942 | function queryDumpFiles(hash) {{ 943 | html = '
CreateTimeExitTimeHandlesImageFileNamePIDPPIDSessionIdThreadsWow64
'; 944 | document.getElementById("resultTable").innerHTML = html; 945 | 946 | $("#tree-container").empty(); 947 | 948 | var myTable = $("#onTable").dataTable({{ 949 | "pageLength": 50, 950 | "fnRowCallback": function(nRow, aData, iDisplayIndex) {{ 951 | $('td:eq(4)', nRow).html('' + aData["PID"] + ''); 952 | return nRow; 953 | }}, 954 | ajax: {{ 955 | "url": "windows.pslist.PsList.json", 956 | "type": "GET", 957 | "dataSrc": "", 958 | }}, 959 | columns: [ 960 | {{data: 'CreateTime'}}, 961 | {{data: 'ExitTime'}}, 962 | {{data: 'Handles'}}, 963 | {{data: 'ImageFileName'}}, 964 | {{data: 'PID'}}, 965 | {{data: 'PPID'}}, 966 | {{data: 'SessionId'}}, 967 | {{data: 'Threads'}}, 968 | {{data: 'Wow64'}}, 969 | ], 970 | }}); 971 | }} 972 | 973 | 974 | function queryPsTree() {{ 975 | jsonTemplate = '{{"CreateTime": "", "ImageFileName": "TOP", "PID": "", "PPID": "", "Wow64": "", "children": __}}'; 976 | 977 | $("#resultTable").empty(); 978 | 979 | d3.json("windows.pstree.PsTree.json", function(error, json) {{ 980 | var jsonStr = JSON.stringify(json).replace(/__children/g, 'children'); 981 | treeBoxes('', JSON.parse(jsonTemplate.replace('__', jsonStr))); 982 | }}); 983 | }} 984 | 985 | 986 | function treeBoxes(urlService, jsonData) {{ 987 | var urlService_ = ''; 988 | 989 | var blue = '#337ab7', 990 | green = '#5cb85c', 991 | yellow = '#f0ad4e', 992 | blueText = '#4ab1eb', 993 | purple = '#9467bd'; 994 | 995 | var margin = {{ 996 | top : 0, 997 | right : 0, 998 | bottom : 0, 999 | left : 0 1000 | }}, 1001 | // Height and width are redefined later in function of the size of the tree 1002 | // (after that the data are loaded) 1003 | width = 800 - margin.right - margin.left, 1004 | height = 400 - margin.top - margin.bottom; 1005 | 1006 | // size of the diagram 1007 | var viewerWidth = $(document).width() - margin.right - margin.left; 1008 | var viewerHeight = $(document).height() - margin.top - margin.bottom; 1009 | 1010 | // console.log("width:", width, viewerWidth); 1011 | // console.log("height:", height, viewerHeight); 1012 | 1013 | var rectNode = {{ width : 140, height : 70, textMargin : 5 }}, 1014 | tooltip = {{ width : 150, height : 40, textMargin : 5 }}; 1015 | var i = 0, 1016 | duration = 750, 1017 | root; 1018 | 1019 | var mousedown; // Use to save temporarily 'mousedown.zoom' value 1020 | var mouseWheel, 1021 | mouseWheelName, 1022 | isKeydownZoom = false; 1023 | 1024 | var tree; 1025 | var baseSvg, 1026 | svgGroup, 1027 | nodeGroup, // If nodes are not grouped together, after a click the svg node will be set after his corresponding tooltip and will hide it 1028 | nodeGroupTooltip, 1029 | linkGroup, 1030 | linkGroupToolTip, 1031 | defs; 1032 | 1033 | init(urlService, jsonData); 1034 | 1035 | function init(urlService, jsonData) 1036 | {{ 1037 | urlService_ = urlService; 1038 | if (urlService && urlService.length > 0) 1039 | {{ 1040 | if (urlService.charAt(urlService.length - 1) != '/') 1041 | urlService_ += '/'; 1042 | }} 1043 | 1044 | if (jsonData) 1045 | drawTree(jsonData); 1046 | else 1047 | {{ 1048 | console.error(jsonData); 1049 | alert('Invalides data.'); 1050 | }} 1051 | }} 1052 | 1053 | function drawTree(jsonData) 1054 | {{ 1055 | tree = d3.layout.tree().size([ height, width ]); 1056 | root = jsonData; 1057 | root.fixed = true; 1058 | 1059 | // Dynamically set the height of the main svg container 1060 | // breadthFirstTraversal returns the max number of node on a same level 1061 | // and colors the nodes 1062 | var maxDepth = 0; 1063 | var maxTreeWidth = breadthFirstTraversal(tree.nodes(root), function(currentLevel) {{ 1064 | maxDepth++; 1065 | currentLevel.forEach(function(node) {{ 1066 | // if (node.type == 'type1') // <----------------------------------------------------------------- 1067 | // node.color = blue; 1068 | // if (node.type == 'type2') 1069 | // node.color = green; 1070 | // if (node.type == 'type3') 1071 | // node.color = yellow; 1072 | // if (node.type == 'type4') 1073 | // node.color = purple; 1074 | }}); 1075 | }}); 1076 | height = maxTreeWidth * (rectNode.height + 20) + 20 - margin.right - margin.left; 1077 | width = maxDepth * (rectNode.width * 1.5) - margin.top - margin.bottom; 1078 | tree = d3.layout.tree().size([ height, width ]); 1079 | root.x0 = height / 2; 1080 | root.y0 = 0; 1081 | 1082 | baseSvg = d3.select('#tree-container').append('svg') 1083 | // .attr('width', width + margin.right + margin.left) 1084 | // .attr('height', height + margin.top + margin.bottom) 1085 | .attr('width', viewerWidth) 1086 | .attr('height', viewerHeight) 1087 | .attr('class', 'svgContainer') 1088 | 1089 | .call(d3.behavior.zoom().scaleExtent([0.1, 3]) 1090 | //.scaleExtent([0.5, 1.5]) // Limit the zoom scale 1091 | .on('zoom', zoomAndDrag)); 1092 | 1093 | // Mouse wheel is desactivated, else after a first drag of the tree, wheel event drags the tree (instead of scrolling the window) 1094 | getMouseWheelEvent(); 1095 | d3.select('#tree-container').select('svg').on(mouseWheelName, null); 1096 | d3.select('#tree-container').select('svg').on('dblclick.zoom', null); 1097 | 1098 | svgGroup = baseSvg.append('g') 1099 | .attr('class','drawarea') 1100 | .append('g') 1101 | .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1102 | 1103 | // SVG elements under nodeGroupTooltip could be associated with nodeGroup, 1104 | // same for linkGroupToolTip and linkGroup, 1105 | // but this separation allows to manage the order on which elements are drew 1106 | // and so tooltips are always on top. 1107 | nodeGroup = svgGroup.append('g') 1108 | .attr('id', 'nodes'); 1109 | linkGroup = svgGroup.append('g') 1110 | .attr('id', 'links'); 1111 | linkGroupToolTip = svgGroup.append('g') 1112 | .attr('id', 'linksTooltips'); 1113 | nodeGroupTooltip = svgGroup.append('g') 1114 | .attr('id', 'nodesTooltips'); 1115 | 1116 | defs = baseSvg.append('defs'); 1117 | initArrowDef(); 1118 | initDropShadow(); 1119 | 1120 | update(root); 1121 | }} 1122 | 1123 | function update(source) 1124 | {{ 1125 | // Compute the new tree layout 1126 | var nodes = tree.nodes(root).reverse(), 1127 | links = tree.links(nodes); 1128 | 1129 | // Check if two nodes are in collision on the ordinates axe and move them 1130 | breadthFirstTraversal(tree.nodes(root), collision); 1131 | // Normalize for fixed-depth 1132 | nodes.forEach(function(d) {{ 1133 | d.y = d.depth * (rectNode.width * 1.5); 1134 | }}); 1135 | 1136 | // 1) ******************* Update the nodes ******************* 1137 | var node = nodeGroup.selectAll('g.node').data(nodes, function(d) {{ 1138 | return d.id || (d.id = ++i); 1139 | }}); 1140 | var nodesTooltip = nodeGroupTooltip.selectAll('g').data(nodes, function(d) {{ 1141 | return d.id || (d.id = ++i); 1142 | }}); 1143 | 1144 | // Enter any new nodes at the parent's previous position 1145 | // We use "insert" rather than "append", so when a new child node is added (after a click) 1146 | // it is added at the top of the group, so it is drawed first 1147 | // else the nodes tooltips are drawed before their children nodes and they 1148 | // hide them 1149 | var nodeEnter = node.enter().insert('g', 'g.node') 1150 | .attr('class', 'node') 1151 | .attr('transform', function(d) {{ 1152 | return 'translate(' + source.y0 + ',' + source.x0 + ')'; }}) 1153 | .on('click', function(d) {{ 1154 | click(d); 1155 | }}); 1156 | var nodeEnterTooltip = nodesTooltip.enter().append('g') 1157 | .attr('transform', function(d) {{ 1158 | return 'translate(' + source.y0 + ',' + source.x0 + ')'; }}); 1159 | 1160 | nodeEnter.append('g').append('rect') 1161 | .attr('rx', 6) 1162 | .attr('ry', 6) 1163 | .attr('width', rectNode.width) 1164 | .attr('height', rectNode.height) 1165 | .attr('class', 'node-rect') 1166 | .attr('fill', function (d) {{ return d.color; }}) 1167 | .attr('filter', 'url(#drop-shadow)'); 1168 | 1169 | nodeEnter.append('foreignObject') 1170 | .attr('x', rectNode.textMargin) 1171 | .attr('y', rectNode.textMargin) 1172 | .attr('width', function() {{ 1173 | return (rectNode.width - rectNode.textMargin * 2) < 0 ? 0 1174 | : (rectNode.width - rectNode.textMargin * 2) 1175 | }}) 1176 | .attr('height', function() {{ 1177 | return (rectNode.height - rectNode.textMargin * 2) < 0 ? 0 1178 | : (rectNode.height - rectNode.textMargin * 2) 1179 | }}) 1180 | .append('xhtml').html(function(d) {{ // <=========================================================================================== 1181 | return '
' 1184 | + '' + d.ImageFileName + '
' 1185 | + 'PID: ' + d.PID + '
' 1186 | + 'PPID: ' + d.PPID + '
' 1187 | + 'Wow64: ' + d.Wow64 + '
' 1188 | + 'CreateTime: ' + d.CreateTime + '
' 1189 | + '
'; 1190 | }}) 1191 | .on('mouseover', function(d) {{ 1192 | $('#nodeInfoID' + d.id).css('visibility', 'visible'); 1193 | $('#nodeInfoTextID' + d.id).css('visibility', 'visible'); 1194 | }}) 1195 | .on('mouseout', function(d) {{ 1196 | $('#nodeInfoID' + d.id).css('visibility', 'hidden'); 1197 | $('#nodeInfoTextID' + d.id).css('visibility', 'hidden'); 1198 | }}); 1199 | 1200 | // Transition nodes to their new position. 1201 | var nodeUpdate = node.transition().duration(duration) 1202 | .attr('transform', function(d) {{ return 'translate(' + d.y + ',' + d.x + ')'; }}); 1203 | nodesTooltip.transition().duration(duration) 1204 | .attr('transform', function(d) {{ return 'translate(' + d.y + ',' + d.x + ')'; }}); 1205 | 1206 | nodeUpdate.select('rect') 1207 | .attr('class', function(d) {{ return d._children ? 'node-rect-closed' : 'node-rect'; }}); 1208 | 1209 | nodeUpdate.select('text').style('fill-opacity', 1); 1210 | 1211 | // Transition exiting nodes to the parent's new position 1212 | var nodeExit = node.exit().transition().duration(duration) 1213 | .attr('transform', function(d) {{ return 'translate(' + source.y + ',' + source.x + ')'; }}) 1214 | .remove(); 1215 | 1216 | nodeExit.select('text').style('fill-opacity', 1e-6); 1217 | 1218 | 1219 | // 2) ******************* Update the links ******************* 1220 | var link = linkGroup.selectAll('path').data(links, function(d) {{ 1221 | return d.target.id; 1222 | }}); 1223 | var linkTooltip = linkGroupToolTip.selectAll('g').data(links, function(d) {{ 1224 | return d.target.id; 1225 | }}); 1226 | 1227 | d3.selection.prototype.moveToFront = function() {{ 1228 | return this.each(function(){{ 1229 | this.parentNode.appendChild(this); 1230 | }}); 1231 | }}; 1232 | 1233 | // Enter any new links at the parent's previous position. 1234 | // Enter any new links at the parent's previous position. 1235 | var linkenter = link.enter().insert('path', 'g') 1236 | .attr('class', 'link') 1237 | .attr('id', function(d) {{ return 'linkID' + d.target.id; }}) 1238 | .attr('d', function(d) {{ return diagonal(d); }}) 1239 | .attr('marker-end', 'url(#end-arrow)') 1240 | .on('mouseover', function(d) {{ 1241 | d3.select(this).moveToFront(); 1242 | d3.select(this).attr('marker-end', 'url(#end-arrow-selected)'); 1243 | d3.select(this).attr('class', 'linkselected'); 1244 | 1245 | }}) 1246 | .on('mouseout', function(d) {{ 1247 | d3.select(this).attr('marker-end', 'url(#end-arrow)'); 1248 | d3.select(this).attr('class', 'link'); 1249 | }}); 1250 | 1251 | // Transition links to their new position. 1252 | var linkUpdate = link.transition().duration(duration) 1253 | .attr('d', function(d) {{ return diagonal(d); }}); 1254 | 1255 | // Transition exiting nodes to the parent's new position. 1256 | link.exit().transition() 1257 | .remove(); 1258 | 1259 | // Stash the old positions for transition. 1260 | nodes.forEach(function(d) {{ 1261 | d.x0 = d.x; 1262 | d.y0 = d.y; 1263 | }}); 1264 | }} 1265 | 1266 | // Zoom functionnality is desactivated (user can use browser Ctrl + mouse wheel shortcut) 1267 | function zoomAndDrag() {{ 1268 | //var scale = d3.event.scale, 1269 | var scale = 1, 1270 | translation = d3.event.translate, 1271 | tbound = -height * scale, 1272 | bbound = height * scale, 1273 | lbound = (-width + margin.right) * scale, 1274 | rbound = (width - margin.left) * scale; 1275 | // limit translation to thresholds 1276 | translation = [ 1277 | Math.max(Math.min(translation[0], rbound), lbound), 1278 | Math.max(Math.min(translation[1], bbound), tbound) 1279 | ]; 1280 | d3.select('.drawarea') 1281 | .attr('transform', 'translate(' + translation + ')' + 1282 | ' scale(' + scale + ')'); 1283 | }} 1284 | 1285 | // Toggle children on click. 1286 | function click(d) {{ 1287 | if (d.children) {{ 1288 | d._children = d.children; 1289 | d.children = null; 1290 | }} else {{ 1291 | d.children = d._children; 1292 | d._children = null; 1293 | }} 1294 | update(d); 1295 | }} 1296 | 1297 | // Breadth-first traversal of the tree 1298 | // func function is processed on every node of a same level 1299 | // return the max level 1300 | function breadthFirstTraversal(tree, func) 1301 | {{ 1302 | var max = 0; 1303 | if (tree && tree.length > 0) 1304 | {{ 1305 | var currentDepth = tree[0].depth; 1306 | var fifo = []; 1307 | var currentLevel = []; 1308 | 1309 | fifo.push(tree[0]); 1310 | while (fifo.length > 0) {{ 1311 | var node = fifo.shift(); 1312 | if (node.depth > currentDepth) {{ 1313 | func(currentLevel); 1314 | currentDepth++; 1315 | max = Math.max(max, currentLevel.length); 1316 | currentLevel = []; 1317 | }} 1318 | currentLevel.push(node); 1319 | if (node.children) {{ 1320 | for (var j = 0; j < node.children.length; j++) {{ 1321 | fifo.push(node.children[j]); 1322 | }} 1323 | }} 1324 | }} 1325 | func(currentLevel); 1326 | return Math.max(max, currentLevel.length); 1327 | }} 1328 | return 0; 1329 | }} 1330 | 1331 | // x = ordoninates and y = abscissas 1332 | function collision(siblings) {{ 1333 | var minPadding = 5; 1334 | if (siblings) {{ 1335 | for (var i = 0; i < siblings.length - 1; i++) 1336 | {{ 1337 | if (siblings[i + 1].x - (siblings[i].x + rectNode.height) < minPadding) 1338 | siblings[i + 1].x = siblings[i].x + rectNode.height + minPadding; 1339 | }} 1340 | }} 1341 | }} 1342 | 1343 | function removeMouseEvents() {{ 1344 | // Drag and zoom behaviors are temporarily disabled, so tooltip text can be selected 1345 | mousedown = d3.select('#tree-container').select('svg').on('mousedown.zoom'); 1346 | d3.select('#tree-container').select('svg').on("mousedown.zoom", null); 1347 | }} 1348 | 1349 | function reactivateMouseEvents() {{ 1350 | // Reactivate the drag and zoom behaviors 1351 | d3.select('#tree-container').select('svg').on('mousedown.zoom', mousedown); 1352 | }} 1353 | 1354 | // Name of the event depends of the browser 1355 | function getMouseWheelEvent() {{ 1356 | if (d3.select('#tree-container').select('svg').on('wheel.zoom')) 1357 | {{ 1358 | mouseWheelName = 'wheel.zoom'; 1359 | return d3.select('#tree-container').select('svg').on('wheel.zoom'); 1360 | }} 1361 | if (d3.select('#tree-container').select('svg').on('mousewheel.zoom') != null) 1362 | {{ 1363 | mouseWheelName = 'mousewheel.zoom'; 1364 | return d3.select('#tree-container').select('svg').on('mousewheel.zoom'); 1365 | }} 1366 | if (d3.select('#tree-container').select('svg').on('DOMMouseScroll.zoom')) 1367 | {{ 1368 | mouseWheelName = 'DOMMouseScroll.zoom'; 1369 | return d3.select('#tree-container').select('svg').on('DOMMouseScroll.zoom'); 1370 | }} 1371 | }} 1372 | 1373 | function diagonal(d) {{ 1374 | var p0 = {{ 1375 | x : d.source.x + rectNode.height / 2, 1376 | y : (d.source.y + rectNode.width) 1377 | }}, p3 = {{ 1378 | x : d.target.x + rectNode.height / 2, 1379 | y : d.target.y - 12 // -12, so the end arrows are just before the rect node 1380 | }}, m = (p0.y + p3.y) / 2, p = [ p0, {{ 1381 | x : p0.x, 1382 | y : m 1383 | }}, {{ 1384 | x : p3.x, 1385 | y : m 1386 | }}, p3 ]; 1387 | p = p.map(function(d) {{ 1388 | return [ d.y, d.x ]; 1389 | }}); 1390 | return 'M' + p[0] + 'C' + p[1] + ' ' + p[2] + ' ' + p[3]; 1391 | }} 1392 | 1393 | function initDropShadow() {{ 1394 | var filter = defs.append("filter") 1395 | .attr("id", "drop-shadow") 1396 | .attr("color-interpolation-filters", "sRGB"); 1397 | 1398 | filter.append("feOffset") 1399 | .attr("result", "offOut") 1400 | .attr("in", "SourceGraphic") 1401 | .attr("dx", 0) 1402 | .attr("dy", 0); 1403 | 1404 | filter.append("feGaussianBlur") 1405 | .attr("stdDeviation", 2); 1406 | 1407 | filter.append("feOffset") 1408 | .attr("dx", 2) 1409 | .attr("dy", 2) 1410 | .attr("result", "shadow"); 1411 | 1412 | filter.append("feComposite") 1413 | .attr("in", 'offOut') 1414 | .attr("in2", 'shadow') 1415 | .attr("operator", "over"); 1416 | }} 1417 | 1418 | function initArrowDef() {{ 1419 | // Build the arrows definitions 1420 | // End arrow 1421 | defs.append('marker') 1422 | .attr('id', 'end-arrow') 1423 | .attr('viewBox', '0 -5 10 10') 1424 | .attr('refX', 0) 1425 | .attr('refY', 0) 1426 | .attr('markerWidth', 6) 1427 | .attr('markerHeight', 6) 1428 | .attr('orient', 'auto') 1429 | .attr('class', 'arrow') 1430 | .append('path') 1431 | .attr('d', 'M0,-5L10,0L0,5'); 1432 | 1433 | // End arrow selected 1434 | defs.append('marker') 1435 | .attr('id', 'end-arrow-selected') 1436 | .attr('viewBox', '0 -5 10 10') 1437 | .attr('refX', 0) 1438 | .attr('refY', 0) 1439 | .attr('markerWidth', 6) 1440 | .attr('markerHeight', 6) 1441 | .attr('orient', 'auto') 1442 | .attr('class', 'arrowselected') 1443 | .append('path') 1444 | .attr('d', 'M0,-5L10,0L0,5'); 1445 | 1446 | // Start arrow 1447 | defs.append('marker') 1448 | .attr('id', 'start-arrow') 1449 | .attr('viewBox', '0 -5 10 10') 1450 | .attr('refX', 0) 1451 | .attr('refY', 0) 1452 | .attr('markerWidth', 6) 1453 | .attr('markerHeight', 6) 1454 | .attr('orient', 'auto') 1455 | .attr('class', 'arrow') 1456 | .append('path') 1457 | .attr('d', 'M10,-5L0,0L10,5'); 1458 | 1459 | // Start arrow selected 1460 | defs.append('marker') 1461 | .attr('id', 'start-arrow-selected') 1462 | .attr('viewBox', '0 -5 10 10') 1463 | .attr('refX', 0) 1464 | .attr('refY', 0) 1465 | .attr('markerWidth', 6) 1466 | .attr('markerHeight', 6) 1467 | .attr('orient', 'auto') 1468 | .attr('class', 'arrowselected') 1469 | .append('path') 1470 | .attr('d', 'M10,-5L0,0L10,5'); 1471 | }} 1472 | }} 1473 | 1474 | function runAPI(hash, plugin) {{ 1475 | var request = new XMLHttpRequest(); 1476 | 1477 | request.open('GET', '{api_gateway_url}?hash=' + hash + '&plugin=' + plugin, false); 1478 | request.send(); 1479 | }} 1480 | -------------------------------------------------------------------------------- /terraform/api.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_rest_api" "api" { 2 | name = "${local.name}-api" 3 | } 4 | 5 | resource "aws_api_gateway_resource" "analysis" { 6 | rest_api_id = aws_api_gateway_rest_api.api.id 7 | parent_id = aws_api_gateway_rest_api.api.root_resource_id 8 | path_part = "analysis" 9 | } 10 | 11 | resource "aws_api_gateway_method" "analysis" { 12 | rest_api_id = aws_api_gateway_rest_api.api.id 13 | resource_id = aws_api_gateway_resource.analysis.id 14 | api_key_required = "false" 15 | authorization = "NONE" 16 | http_method = "GET" 17 | } 18 | 19 | resource "aws_api_gateway_method_response" "analysis" { 20 | rest_api_id = aws_api_gateway_rest_api.api.id 21 | resource_id = aws_api_gateway_resource.analysis.id 22 | http_method = aws_api_gateway_method.analysis.http_method 23 | status_code = "200" 24 | 25 | response_models = { 26 | "text/html" = "Empty" 27 | } 28 | 29 | response_parameters = { 30 | "method.response.header.Access-Control-Allow-Headers" = "false" 31 | "method.response.header.Access-Control-Allow-Methods" = "false" 32 | "method.response.header.Access-Control-Allow-Origin" = "false" 33 | } 34 | depends_on = [aws_api_gateway_method.analysis] 35 | } 36 | 37 | resource "aws_api_gateway_integration" "analysis" { 38 | connection_type = "INTERNET" 39 | content_handling = "CONVERT_TO_TEXT" 40 | http_method = aws_api_gateway_method.analysis.http_method 41 | integration_http_method = "POST" 42 | passthrough_behavior = "WHEN_NO_MATCH" 43 | resource_id = aws_api_gateway_resource.analysis.id 44 | rest_api_id = aws_api_gateway_rest_api.api.id 45 | type = "AWS_PROXY" 46 | uri = aws_lambda_function.function_plugin.invoke_arn 47 | } 48 | 49 | resource "aws_api_gateway_integration_response" "analysis" { 50 | http_method = aws_api_gateway_method.analysis.http_method 51 | resource_id = aws_api_gateway_resource.analysis.id 52 | 53 | response_parameters = { 54 | "method.response.header.Access-Control-Allow-Origin" = "'*'" 55 | } 56 | 57 | rest_api_id = aws_api_gateway_rest_api.api.id 58 | status_code = "200" 59 | 60 | depends_on = [aws_api_gateway_integration.analysis] 61 | } 62 | 63 | resource "aws_api_gateway_deployment" "api" { 64 | rest_api_id = aws_api_gateway_rest_api.api.id 65 | stage_name = "analysis" 66 | stage_description = "timestamp = ${timestamp()}" 67 | 68 | depends_on = [ 69 | aws_api_gateway_integration.analysis 70 | ] 71 | 72 | lifecycle { 73 | create_before_destroy = true 74 | } 75 | } 76 | 77 | resource "aws_api_gateway_stage" "api" { 78 | deployment_id = aws_api_gateway_deployment.api.id 79 | cache_cluster_enabled = "false" 80 | rest_api_id = aws_api_gateway_rest_api.api.id 81 | stage_name = "v1" 82 | xray_tracing_enabled = "false" 83 | } 84 | 85 | #resource "aws_api_gateway_method_settings" "api" { 86 | # rest_api_id = aws_api_gateway_rest_api.api.id 87 | # stage_name = aws_api_gateway_stage.api.stage_name 88 | # method_path = "*/*" 89 | # 90 | # settings { 91 | # data_trace_enabled = true 92 | # logging_level = "INFO" 93 | # } 94 | #} 95 | 96 | resource "aws_lambda_permission" "analysis" { 97 | action = "lambda:InvokeFunction" 98 | function_name = "${local.function_name}_plugin" 99 | principal = "apigateway.amazonaws.com" 100 | source_arn = "${aws_api_gateway_rest_api.api.execution_arn}/*/${aws_api_gateway_method.analysis.http_method}/${aws_api_gateway_resource.analysis.path_part}" 101 | 102 | depends_on = [aws_lambda_function.function_plugin] 103 | } 104 | 105 | resource "aws_api_gateway_rest_api_policy" "analysis" { 106 | rest_api_id = aws_api_gateway_rest_api.api.id 107 | 108 | policy = jsonencode({ 109 | Version = "2012-10-17" 110 | Statement = [ 111 | { 112 | Effect = "Allow" 113 | Principal = "*" 114 | Action = "execute-api:Invoke" 115 | Resource = [ 116 | "${aws_api_gateway_rest_api.api.execution_arn}/*", 117 | ] 118 | Condition = { 119 | IpAddress = { 120 | "aws:SourceIp" = var.trusted_ip 121 | } 122 | } 123 | }, 124 | ] 125 | }) 126 | } 127 | -------------------------------------------------------------------------------- /terraform/batch.tf: -------------------------------------------------------------------------------- 1 | resource "aws_batch_job_definition" "analysis" { 2 | name = "${local.name}_job" 3 | type = "container" 4 | platform_capabilities = [ 5 | "FARGATE", 6 | ] 7 | container_properties = < (Job name: )\"" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /terraform/codecommit.tf: -------------------------------------------------------------------------------- 1 | resource "aws_codecommit_repository" "analysis" { 2 | repository_name = "${local.name}-repo" 3 | } 4 | 5 | resource "null_resource" "push_pruebascode" { 6 | triggers = { 7 | file1 = "${sha256(file("analysis_script/analysis.py"))}" 8 | file2 = "${sha256(file("analysis_script/template.css"))}" 9 | file3 = "${sha256(file("analysis_script/template.html"))}" 10 | file4 = "${sha256(file("analysis_script/template.js"))}" 11 | } 12 | 13 | provisioner "local-exec" { 14 | command = < 2 | 3 | 4 | Analysis Results 5 | 6 | 10 | 11 | 12 | 19 |
20 |
21 |
22 |
23 |
24 | Processing... 25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /terraform/sns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sns_topic" "analysis" { 2 | name = "${local.name}_topic" 3 | delivery_policy = jsonencode({ 4 | "http" : { 5 | "defaultHealthyRetryPolicy" : { 6 | "minDelayTarget" : 20, 7 | "maxDelayTarget" : 20, 8 | "numRetries" : 3, 9 | "numMaxDelayRetries" : 0, 10 | "numNoDelayRetries" : 0, 11 | "numMinDelayRetries" : 0, 12 | "backoffFunction" : "linear" 13 | }, 14 | "disableSubscriptionOverrides" : false, 15 | "defaultThrottlePolicy" : { 16 | "maxReceivesPerSecond" : 1 17 | } 18 | } 19 | }) 20 | } 21 | 22 | resource "aws_sns_topic_subscription" "analysis" { 23 | topic_arn = aws_sns_topic.analysis.arn 24 | protocol = "email" 25 | endpoint = var.sns_email 26 | } 27 | 28 | resource "aws_sns_topic_policy" "default" { 29 | arn = aws_sns_topic.analysis.arn 30 | 31 | policy = <