feature: dev (shellcode projects) phase 1

This commit is contained in:
Dobin
2024-03-26 17:46:09 +00:00
parent aa194edef3
commit f08334dc1a
16 changed files with 285 additions and 55 deletions
+2 -1
View File
@@ -16,4 +16,5 @@ tools/
doc/ doc/
*.pickle *.pickle
logs/ logs/
app/projects/* app/projects/*
data/dev/*
+31
View File
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
{% include 'header.html' %}
</head>
<body>
{% include 'navigation.html' %}
<div class="indent">
<h1> ShcDev: {{name}}</h1>
<table class="table">
{% for file in files %}
<tr>
<td><a href="/dev/{{name}}/file/{file['name']}">{{ file['name']}}</a></td>
<td>{{file["date"]}}</td>
<td>{{file["info"]}}</td>
</tr>
{% endfor %}
</table>
<a href="/dev/{{name}}/build">Build</a>
<br><hr>
<pre>{{log}}</pre>
</div>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
{% include 'header.html' %}
</head>
<body>
{% include 'navigation.html' %}
<div class="indent">
<h1> ShcDevs: </h1>
<ul>
{% for item in data %}
<li><a href="/dev/{{item['name']}}">{{ item['name'] }}</a>
({{item["date"]}})
</li>
{% endfor %}
</ul>
</div>
</body>
</html>
-8
View File
@@ -10,14 +10,6 @@
<h1> SuperMega </h1> <h1> SuperMega </h1>
<!-- iterate through data and print as ul -->
<ul>
{% for item in data %}
<li><a href="/project/{{item.name}}">{{ item.name }}</a></li>
{% endfor %}
</ul>
<a href="/add_project">Add Project</a>
</div> </div>
</body> </body>
</html> </html>
+14 -1
View File
@@ -14,7 +14,20 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item"><a class="nav-link {{ 'active' if request.path == '/' else '' }}" href="/">Home</a></li> <li class="nav-item">
<a class="nav-link {{ 'active' if request.path == '/' else '' }}"
href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link {{ 'active' if request.path == '/projects' else '' }}"
href="/projects">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link {{ 'active' if request.path == '/dev' else '' }}"
href="/dev">ShcDev</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
{% include 'header.html' %}
</head>
<body>
{% include 'navigation.html' %}
<div class="indent">
<h1> Projects </h1>
<ul>
{% for item in data %}
<li><a href="/project/{{item.name}}">{{ item.name }}</a></li>
{% endfor %}
</ul>
<a href="/add_project">Add Project</a>
</div>
</body>
</html>
+84
View File
@@ -11,7 +11,10 @@ import difflib
from ansi2html import Ansi2HTMLConverter from ansi2html import Ansi2HTMLConverter
import shutil import shutil
import subprocess import subprocess
import time
from datetime import datetime
from observer import observer
from config import config from config import config
from model.settings import Settings from model.settings import Settings
from model.defs import * from model.defs import *
@@ -19,7 +22,10 @@ from supermega import start
from app.storage import storage, Project from app.storage import storage, Project
from sender import scannerDetectsBytes from sender import scannerDetectsBytes
from phases.injector import verify_injected_exe from phases.injector import verify_injected_exe
from phases.compiler import compile_dev
from phases.assembler import asm_to_shellcode
from helper import run_process_checkret from helper import run_process_checkret
from log import getlog
views = Blueprint('views', __name__) views = Blueprint('views', __name__)
@@ -37,6 +43,84 @@ def index():
return render_template('index.html', data=storage.data) return render_template('index.html', data=storage.data)
@views.route("/projects")
def projects_route():
return render_template('projects.html', data=storage.data)
@views.route("/dev")
def devs_route():
data = []
path = "data/dev"
for file_path in os.listdir(path):
creation_time = os.path.getctime("data/dev" + "/" + file_path)
readable_time = datetime.fromtimestamp(creation_time).strftime('%Y-%m-%d %H:%M:%S')
data.append({
"name": file_path,
"date": readable_time,
})
return render_template('devs.html', data=data)
@views.route("/dev/<name>")
def dev_route(name):
data = []
log = ""
path = "data/dev/{}".format(name)
for file_path in os.listdir(path):
creation_time = os.path.getctime(path + "/" + file_path)
readable_time = datetime.fromtimestamp(creation_time).strftime('%Y-%m-%d %H:%M:%S')
info = ""
if file_path.endswith(".asm"):
info = "text assembly (cleaned, from compiled .c)"
elif file_path.endswith(".bin"):
info = "generated shellcode (from .exe)"
elif file_path.endswith(".c"):
info = "input C code"
elif file_path.endswith(".exe"):
info = "temporary shellcode holder (from .c)"
elif file_path.endswith(".log"):
info = "log file"
with open(path + "/" + file_path, "r") as f:
log = f.read()
print(log)
data.append({
"name": file_path,
"date": readable_time,
"info": info,
})
return render_template('dev.html',
name=name, files=data, log=log)
@views.route("/dev/<name>/build")
def dev_build_route(name):
c_in = "data/dev/{}/main.c".format(name)
asm_out = "data/dev/{}/main.asm".format(name)
build_exe = "data/dev/{}/main.exe".format(name)
shellcode_out = "data/dev/{}/main.bin".format(name)
log = "data/dev/{}/main.log".format(name)
compile_dev(c_in, asm_out)
asm_to_shellcode(asm_out, build_exe, shellcode_out)
with open(log, "w") as f:
for log_line in getlog():
f.write("{}\n".format(log_line))
f.write("\n\n")
for log in observer.logs:
f.write("{}".format(log))
return redirect("/dev/{}".format(name), code=302)
@views.route("/project/<name>") @views.route("/project/<name>")
def project(name): def project(name):
project = storage.get_project(name) project = storage.get_project(name)
+8 -39
View File
@@ -6,6 +6,7 @@ import logging
from config import config from config import config
from model.defs import * from model.defs import *
from observer import observer
logger = logging.getLogger("Helper") logger = logging.getLogger("Helper")
@@ -48,22 +49,28 @@ def run_process_checkret(args, check=True):
except Exception as e: except Exception as e:
logger.warn(f"An error occurred: {e}") logger.warn(f"An error occurred: {e}")
# Handle other exceptions # Handle other exceptions
with open(f"{logs_dir}/cmdoutput.log", "ab") as f: with open(f"{logs_dir}/cmdoutput.log", "ab") as f:
cmd = "------------------------------------\n" cmd = "------------------------------------\n"
cmd += "--- " + " ".join(args) + "\n" cmd += "--- " + " ".join(args) + "\n"
f.write(cmd.encode('utf-8')) f.write(cmd.encode('utf-8'))
if ret.stdout != None: if ret.stdout != None:
observer.add_log(ret.stdout.decode('utf-8'))
f.write(ret.stdout) f.write(ret.stdout)
if ret.stderr != None: if ret.stderr != None:
observer.add_log(ret.stderr.decode('utf-8'))
f.write(ret.stderr) f.write(ret.stderr)
if ret.returncode != 0 and check: if ret.returncode != 0 and check:
logger.info("----! FAILED Command: {}".format(" ".join(args))) logger.info("----! FAILED Command: {}".format(" ".join(args)))
if ret.stdout != None: if ret.stdout != None:
observer.add_log(ret.stdout.decode('utf-8'))
logger.info(ret.stdout.decode('utf-8')) logger.info(ret.stdout.decode('utf-8'))
if ret.stderr != None: if ret.stderr != None:
observer.add_log(ret.stderr.decode('utf-8'))
logger.info(ret.stderr.decode('utf-8')) logger.info(ret.stderr.decode('utf-8'))
raise Exception("Command failed: " + " ".join(args)) raise Exception("Command failed: " + " ".join(args))
if config.ShowCommandOutput: if config.ShowCommandOutput:
logger.info("> " + " ".join(args)) logger.info("> " + " ".join(args))
if ret.stdout != None: if ret.stdout != None:
@@ -92,16 +99,6 @@ def file_readall_binary(filepath) -> bytes:
return data return data
def delete_all_files_in_directory(directory_path):
files = glob.glob(os.path.join(directory_path, '*'))
for file_path in files:
try:
os.remove(file_path)
#logger.info(f"Deleted {file_path}")
except Exception as e:
logger.info(f"Error deleting {file_path}: {e}")
def rbrunmode_str(rbrunmode): def rbrunmode_str(rbrunmode):
rbrunmode = str(rbrunmode) rbrunmode = str(rbrunmode)
if rbrunmode == "1": if rbrunmode == "1":
@@ -112,34 +109,6 @@ def rbrunmode_str(rbrunmode):
return "Invalid: {}".format(rbrunmode) return "Invalid: {}".format(rbrunmode)
def hexdump(data, addr = 0, num = 0):
s = ''
n = 0
lines = []
if num == 0: num = len(data)
if len(data) == 0:
return '<empty>'
for i in range(0, num, 16):
line = ''
line += '%04x | ' % (addr + i)
n += 16
for j in range(n-16, n):
if j >= len(data): break
line += '%02x ' % (data[j] & 0xff)
line += ' ' * (3 * 16 + 7 - len(line)) + ' | '
for j in range(n-16, n):
if j >= len(data): break
c = data[j] if not (data[j] < 0x20 or data[j] > 0x7e) else '.'
line += '%c' % c
lines.append(line)
return '\n'.join(lines)
def file_to_lf(filename): def file_to_lf(filename):
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
+3
View File
@@ -52,6 +52,9 @@ def writelog():
for line in log_messages: for line in log_messages:
f.write(line + "\n") f.write(line + "\n")
def getlog():
return log_messages
def setup_logging(level = logging.INFO): def setup_logging(level = logging.INFO):
root_logger = logging.getLogger() root_logger = logging.getLogger()
root_logger.setLevel(level) root_logger.setLevel(level)
+4 -2
View File
@@ -2,9 +2,8 @@ import json
import pprint import pprint
from capstone import Cs, CS_ARCH_X86, CS_MODE_64 from capstone import Cs, CS_ARCH_X86, CS_MODE_64
from model import *
from pe.r2helper import r2_disas from pe.r2helper import r2_disas
from helper import delete_all_files_in_directory from utils import delete_all_files_in_directory
from model.defs import * from model.defs import *
@@ -18,6 +17,9 @@ class Observer():
self.logs = [] self.logs = []
self.idx = 0 self.idx = 0
def add_log(self, log):
self.logs.append(log)
def add_text(self, name, data): def add_text(self, name, data):
self.write_to_file(name + ".txt", data) self.write_to_file(name + ".txt", data)
self.idx += 1 self.idx += 1
+1 -1
View File
@@ -11,7 +11,7 @@ import keystone
from enum import IntEnum from enum import IntEnum
import logging import logging
from helper import hexdump from utils import hexdump
from pe.superpe import SuperPe from pe.superpe import SuperPe
from model.defs import * from model.defs import *
+1 -1
View File
@@ -2,7 +2,7 @@ import r2pipe
import os import os
from model.defs import * from model.defs import *
from helper import hexdump from utils import hexdump
def r2_disas(data: bytes): def r2_disas(data: bytes):
filename = "r2_data.bin" filename = "r2_data.bin"
+1 -1
View File
@@ -4,7 +4,7 @@ from enum import IntEnum
import logging import logging
from typing import List from typing import List
from helper import hexdump from utils import hexdump
from model.defs import * from model.defs import *
logger = logging.getLogger("superpe") logger = logging.getLogger("superpe")
+41
View File
@@ -16,6 +16,47 @@ from model.exehost import ExeHost
logger = logging.getLogger("Compiler") logger = logging.getLogger("Compiler")
use_templates = True use_templates = True
# NOTE: Mostly copy-pasted from compiler.py::compile()
def compile_dev(
c_in: FilePath,
asm_out: FilePath,
short_call_patching: bool = False,
):
logger.info("--[ Compile C to ASM: {} -> {} ".format(c_in, asm_out))
# Compile C To Assembly (text)
run_process_checkret([
config.get("path_cl"),
"/c",
"/FA",
"/GS-",
"/Fa{}/".format(os.path.dirname(c_in)),
c_in,
])
if not os.path.isfile(asm_out):
raise Exception("Error: Compiling failed")
file_to_lf(asm_out)
observer.add_text("carrier_asm_orig", file_readall_text(asm_out))
# Assembly cleanup (masm_shc)
asm_clean_file = asm_out + ".clean"
logger.info("---[ ASM masm_shc: {} ".format(asm_out))
params = Params(asm_out, asm_clean_file,
inline_strings=False, # not for DATA_REUSE
remove_crt=True,
append_rsp_stub=True) # required atm
process_file(params)
if not os.path.isfile(asm_clean_file):
raise Exception("Error: Cleaned up ASM file {} was not created".format(
asm_clean_file
))
# Move to destination we expect
shutil.move(asm_clean_file, asm_out)
if config.debug:
observer.add_text("carrier_asm_cleanup", file_readall_text(asm_out))
def compile( def compile(
c_in: FilePath, c_in: FilePath,
+1 -1
View File
@@ -18,7 +18,7 @@ from model.project import Project
from model.settings import Settings from model.settings import Settings
from model.defs import * from model.defs import *
from log import setup_logging, writelog from log import setup_logging, writelog
from utils import delete_all_files_in_directory
def main(): def main():
"""Argument parsing for when called from command line""" """Argument parsing for when called from command line"""
+49
View File
@@ -0,0 +1,49 @@
import subprocess
import os
import pathlib
import glob
import logging
from config import config
from model.defs import *
logger = logging.getLogger("Utils")
def delete_all_files_in_directory(directory_path):
files = glob.glob(os.path.join(directory_path, '*'))
for file_path in files:
try:
os.remove(file_path)
#logger.info(f"Deleted {file_path}")
except Exception as e:
logger.info(f"Error deleting {file_path}: {e}")
def hexdump(data, addr = 0, num = 0):
s = ''
n = 0
lines = []
if num == 0: num = len(data)
if len(data) == 0:
return '<empty>'
for i in range(0, num, 16):
line = ''
line += '%04x | ' % (addr + i)
n += 16
for j in range(n-16, n):
if j >= len(data): break
line += '%02x ' % (data[j] & 0xff)
line += ' ' * (3 * 16 + 7 - len(line)) + ' | '
for j in range(n-16, n):
if j >= len(data): break
c = data[j] if not (data[j] < 0x20 or data[j] > 0x7e) else '.'
line += '%c' % c
lines.append(line)
return '\n'.join(lines)