├── .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 | 
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 |
40 | -
41 |
44 |
49 |
50 | -
51 |
54 |
66 |
67 | -
68 |
71 |
80 |
81 | -
82 |
85 |
93 |
94 | -
95 |
98 |
104 |
105 | -
106 |
109 |
115 |
116 | -
117 |
120 |
137 |
138 | -
139 |
142 |
148 |
149 | -
150 |
153 |
164 |
165 |
166 |
167 |
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 Date | Changed Date | Created Date | Description | Modified Date | Plugin |
---|
';
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 = 'Allocation | NumberOfBytes | PoolType | Tag |
---|
';
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 = 'Username | Domain | Domain name | Hash |
---|
';
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 = 'Callback | Detail | Module | Symbol | Type |
---|
';
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 = '';
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 = 'Base | LoadTime | Name | PID | Path | Process | Size |
---|
';
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 = 'Address | Driver Name | IRP | Module | Symbol |
---|
';
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 Name | Name | Service Key | Size | Start |
---|
';
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 = 'Block | PID | Process | Value | Variable |
---|
';
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 = '';
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 = '';
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 = '';
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 = '';
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 = '';
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 = 'Disasm | Hexdump | PID | Process | Tag |
---|
';
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 = '';
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 = '';
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 = '';
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 = 'Created | ForeignAddr | ForeignPort | LocalAddr | LocalPort | Owner | PID | Proto | State |
---|
';
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 = 'Created | ForeignAddr | ForeignPort | LocalAddr | LocalPort | Owner | PID | Proto | State |
---|
';
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 = '';
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 = 'CreateTime | ExitTime | Handles | ImageFileName | PID | PPID | SessionId | Threads | Wow64 |
---|
';
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 = 'CreateTime | ExitTime | Handles | ImageFileName | PID | PPID | SessionId | Threads | Wow64 |
---|
';
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 = 'Attributes | Description | PID | Privilege | Process | Value |
---|
';
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 path | Certificate section | Certificate ID | Certificate 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 = '';
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 = '';
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 = '';
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 Time | Type | Key | Name | Data |
---|
';
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 = 'PID | Process | Skeleton Key Found | rc4HmacDecrypt | rc4HmacInitialize |
---|
';
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 = '';
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 = 'Binary | Display | Name | Order | PID | Start | State | Type |
---|
';
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 = 'CreateTime | From Name | To 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 = 'CommitCharge | End VPN | File | PID | Parent | PrivateMemory | Process | Protection | Start VPN | Tag |
---|
';
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 = 'Base | Build | Major | Minor | Name | PID | Process | Product |
---|
';
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 offset | End offset | Region |
---|
';
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 = 'Offset | PID | Rule | Component | Value |
---|
';
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 = 'ImageFileName | Module Base | Module Name | PID | imphash |
---|
';
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 = '';
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 = 'ImageFileName | Module Base | Module Name | PID | impfuzzy |
---|
';
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 = '';
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 = 'CreateTime | ExitTime | Handles | ImageFileName | PID | PPID | SessionId | Threads | Wow64 |
---|
';
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 |