From f338424d50a4cc7c414926f3e230a4e5358959bc Mon Sep 17 00:00:00 2001 From: Dobin Date: Mon, 5 Feb 2024 12:28:38 +0000 Subject: [PATCH] refactor: better debug and get in-between step results --- helper.py | 89 ++++++++++++++++++++++++------------- supermega.py | 122 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 143 insertions(+), 68 deletions(-) diff --git a/helper.py b/helper.py index 7aabb60..6ade52d 100644 --- a/helper.py +++ b/helper.py @@ -20,7 +20,7 @@ build_dir = "build" def clean_files(): - print("--[ Cleanup files ]") + print("--[ Remove old files ]") files_to_clean = [ # compile artefacts in current dir @@ -30,19 +30,27 @@ def clean_files(): # out/ stuff os.path.join(build_dir, "main.asm"), - os.path.join(build_dir, "main-clean.asm"), - os.path.join(build_dir, "main-clean.bin"), - os.path.join(build_dir, "main-clean-append.bin"), + os.path.join(build_dir, "main.bin"), + os.path.join(build_dir, "main.c"), + #os.path.join(build_dir, "main.exe"), verify_filename, - #"main-clean.exe", # at the end as it may still shutdown? ] for file in files_to_clean: pathlib.Path(file).unlink(missing_ok=True) def make_c_to_asm(c_file, asm_file, payload_len): - print("--[ Compile C source to ASM: {} -> {} ]".format(c_file, asm_file)) + print("--[ C to ASM: {} -> {} ]".format(c_file, asm_file)) + + asm = { + "initial": "", + "cleanup": "", + "fixup": "", + } + + # Phase 1: Compile + print("---[ Compile: {} ]".format(c_file)) subprocess.run([ path_cl, "/c", @@ -50,33 +58,36 @@ def make_c_to_asm(c_file, asm_file, payload_len): "/GS-", "/Fa{}/".format(os.path.dirname(c_file)), c_file, - ]) + ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if not os.path.isfile(asm_file): - print("Error") + print("Error: Compiling failed") return - else: - print(" > Generated {}".format(asm_file)) + asm["initial"] = file_readall_text(asm_file) - # need different file + # Phase 2: Assembly cleanup asm_clean_file = asm_file + ".clean" - print("--[ Cleanup ASM: {} -> {} ]".format(asm_file, asm_clean_file)) + print("---[ Cleanup: {} ]".format(asm_file)) subprocess.run([ path_masmshc, asm_file, asm_clean_file, ], check=True, stdout=subprocess.DEVNULL) if not os.path.isfile(asm_clean_file): - print("Error") + print("Error: Cleanup filed") return else: - print(" > Generated {}".format(asm_clean_file)) shutil.move(asm_clean_file, asm_file) + asm["cleanup"] = file_readall_text(asm_file) - print("--[ Fixup ASM: {} ]".format(asm_clean_file)) - fixup_asm_file(asm_file, payload_len) - - input("Press Enter to continue...") + # Phase 2: Assembly fixup + print("---[ Fixup : {} ]".format(asm_file)) + if not fixup_asm_file(asm_file, payload_len): + print("Error: Fixup failed") + return + else: + asm["fixup"] = file_readall_text(asm_file) + return asm def fixup_asm_file(filename, payload_len): @@ -86,7 +97,7 @@ def fixup_asm_file(filename, payload_len): # replace external reference with shellcode reference for idx, line in enumerate(lines): if "dobin" in lines[idx]: - print(" > Replace external reference at: {}".format(idx)) + print(" > Replace external reference at line: {}".format(idx)) lines[idx] = lines[idx].replace( "mov r8, QWORD PTR dobin", "lea r8, [shcstart]" @@ -95,13 +106,14 @@ def fixup_asm_file(filename, payload_len): # replace payload length for idx, line in enumerate(lines): if "11223344" in lines[idx]: + print(" > Replace payload length at line: {}".format(idx)) lines[idx] = lines[idx].replace("11223344", str(payload_len+1)) break # add label at end of code for idx, line in enumerate(lines): if lines[idx].startswith("END"): - print(" > Add end of code label at: {}".format(idx)) + print(" > Add end of code label at line: {}".format(idx)) lines.insert(idx-1, "shcstart:\r\n") lines.insert(idx, "\tnop\r\n") break @@ -109,28 +121,31 @@ def fixup_asm_file(filename, payload_len): with open(filename, 'w') as asmfile: asmfile.writelines(lines) + return True -def make_shc_from_asm(asm_clean_file, exe_file, shc_file): - print("--[ Assemble to exe ]") - print("AAAAAA: {}".format(exe_file)) + +def make_shc_from_asm(asm_file, exe_file, shc_file): + print("--[ Assemble to exe: {} -> {} -> {} ]".format(asm_file, exe_file, shc_file)) + + print("---[ Assemble ASM to EXE: {} -> {} ]".format(asm_file, exe_file)) subprocess.run([ path_ml64, - asm_clean_file, + asm_file, "/link", "/OUT:{}".format(exe_file), "/entry:AlignRSP" - ], check=True) + ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if not os.path.isfile(exe_file): print("Error") return - else: - print(" > Generated {}".format(exe_file)) - print("---[ Get code section from exe ]") + print("---[ EXE to SHC: {} -> {} ]".format(exe_file, shc_file)) code = get_code_section(exe_file) with open(shc_file, 'wb') as f: f.write(code) - print("---[ Shellcode from {} written to: {} (size: {}) ]".format(exe_file, shc_file, len(code))) + + return code + #print("---[ Shellcode from {} written to: {} (size: {}) ]".format(exe_file, shc_file, len(code))) def get_code_section(pe_file): @@ -163,8 +178,8 @@ def remove_trailing_null_bytes(data): return b'' # If the entire sequence is null bytes -def test_shellcode(shc_file): - print("--[ Test it with runshc ]") +def try_start_shellcode(shc_file): + print("--[ Blindly execute shellcode: {} ]".format(shc_file)) subprocess.run([ path_runshc, shc_file, @@ -250,6 +265,18 @@ def verify_injected_exe(exefile): print("---> Verify OK. Infected exe verified (file was created)") # better to remove it immediately os.remove(verify_filename) + return True else: print("---> Verify FAIL. Infected exe did not create file.") + return False + +def file_readall_text(filepath) -> str: + with open(filepath, "r") as f: + data = f.read() + return data + +def file_readall_binary(filepath) -> bytes: + with open(filepath, "rb") as f: + data = f.read() + return data diff --git a/supermega.py b/supermega.py index 90fec3b..10c1b27 100644 --- a/supermega.py +++ b/supermega.py @@ -24,21 +24,26 @@ options_default = { "payload": "shellcodes/calc64.bin", "verify": False, - "cleanup_files_on_start": True, - "cleanup_files_on_exit": True, # all is just in out/ - - - "generate_asm_from_c": True, - "generate_shc_from_asm": True, - "test_loader_shellcode": False, - "obfuscate_shc_loader": False, - "test_obfuscated_shc": False, - "exec_final_shellcode": True, - + # configuration "alloc_style": AllocStyle.RWX, "exec_style": ExecStyle.CALL, "copy_style": CopyStyle.SIMPLE, - "dataref_style": DataRefStyle.APPEND + "dataref_style": DataRefStyle.APPEND, + + "try_start_loader_shellcode": False, # without payload (Debugging) + "try_start_final_shellcode": True, # with payload (should work) + + # cleanup + "cleanup_files_on_start": True, + "cleanup_files_on_exit": True, + + # For debugging: Can disable some steps + "generate_asm_from_c": True, + "generate_shc_from_asm": True, + + # Not working atm + "obfuscate_shc_loader": False, + "test_obfuscated_shc": False, } @@ -51,24 +56,32 @@ options_verify = { "payload": "shellcodes/createfile.bin", "verify": True, - "cleanup_files_on_start": True, - "cleanup_files_on_exit": True, # all is just in out/ + # configuration + "alloc_style": AllocStyle.RWX, + "exec_style": ExecStyle.CALL, + "copy_style": CopyStyle.SIMPLE, + "dataref_style": DataRefStyle.APPEND, - "generate_asm_from_c": True, - "generate_shc_from_asm": True, - "test_loader_shellcode": False, - "obfuscate_shc_loader": False, - "test_obfuscated_shc": False, - "exec_final_shellcode": False, + # testing + "try_start_loader_shellcode": False, # without payload (Debugging) + "try_start_final_shellcode": False, # with payload (should work) + # injecting into exe "inject_exe": True, "inject_exe_in": "exes/procexp64.exe", "inject_exe_out": "out/procexp64-a.exe", - "alloc_style": AllocStyle.RWX, - "exec_style": ExecStyle.CALL, - "copy_style": CopyStyle.SIMPLE, - "dataref_style": DataRefStyle.APPEND + # For debugging: Can disable some steps + "generate_asm_from_c": True, + "generate_shc_from_asm": True, + + # cleanup + "cleanup_files_on_start": True, + "cleanup_files_on_exit": True, # all is just in out/ + + # doesnt work + "obfuscate_shc_loader": False, + "test_obfuscated_shc": False, } @@ -81,6 +94,19 @@ main_exe_file = os.path.join(build_dir, "main.exe") main_shc_file = os.path.join(build_dir, "main.bin") +debug_data = { + "loader_shellcode": b"", + "payload_shellcode": b"", + "final_shellcode": b"", + + "asm_initial": "", + "asm_cleanup": "", + "asm_fixup": "", + + "original_exe": b"", + "infected_exe": b"", +} + def main(): print("Super Mega") @@ -88,17 +114,25 @@ def main(): if options["cleanup_files_on_start"]: clean_files() + shutil.copy("source/main.c", "build/main.c") + shutil.copy("source/peb_lookup.h", "build/peb_lookup.h") + if options["generate_asm_from_c"]: with open(options["payload"], 'rb') as input2: data_payload = input2.read() l = len(data_payload) - make_c_to_asm(main_c_file, main_asm_file, l) + debug_data["payload_shellcode"] = data_payload + asm = make_c_to_asm(main_c_file, main_asm_file, l) + debug_data["asm_initial"] = asm["initial"] + debug_data["asm_cleanup"] = asm["cleanup"] + debug_data["asm_fixup"] = asm["fixup"] if options["generate_asm_from_c"]: - make_shc_from_asm(main_asm_file, main_exe_file, main_shc_file) + code = make_shc_from_asm(main_asm_file, main_exe_file, main_shc_file) + debug_data["loader_shellcode"] = code - if options["test_loader_shellcode"]: - test_shellcode(main_shc_file) + if options["try_start_loader_shellcode"]: + try_start_shellcode(main_shc_file) # SGN seems buggy atm #if options["obfuscate_shc_loader"]: @@ -109,41 +143,55 @@ def main(): # return if options["dataref_style"] == DataRefStyle.APPEND: + print("--[ Merge stager: {} + {} -> {} ] ".format(main_shc_file, options["payload"], main_shc_file)) with open(main_shc_file, 'rb') as input1: data_stager = input1.read() with open(options["payload"], 'rb') as input2: data_payload = input2.read() - print("--[ Integrate Stager: {} Payload: {} (sum: {})]".format( + print("---[ Size: Stager: {} and Payload: {} Sum: {} ]".format( len(data_stager), len(data_payload), len(data_stager)+len(data_payload))) with open(main_shc_file, 'wb') as output: - output.write(data_stager) - output.write(data_payload) - - print("---[ Final shellcode available at: {} ]".format(main_shc_file)) + data = data_stager + data_payload + output.write(data) + debug_data["final_shellcode"] = data if options["verify"]: print("--[ Verify final shellcode ]") if not verify_shellcode(main_shc_file): return - if options["exec_final_shellcode"]: + if options["try_start_final_shellcode"]: print("--[ Test Append shellcode ]") - test_shellcode(main_shc_file) + try_start_shellcode(main_shc_file) # copy it to out - #shutil.copyfile(main_shc_file, os.path.join("out/", os.path.basename(main_bin_clean_append_file))) + shutil.copyfile(main_shc_file, os.path.join("out/", os.path.basename(main_shc_file))) if options["inject_exe"]: + debug_data["original_exe"] = file_readall_binary(options["inject_exe_in"]) + inject_exe(main_shc_file, options["inject_exe_in"], options["inject_exe_out"]) if options["verify"]: print("--[ Verify final exe ]") - verify_injected_exe(options["inject_exe_out"]) + if verify_injected_exe(options["inject_exe_out"]): + debug_data["infected_exe"] = file_readall_binary(options["inject_exe_out"]) if options["cleanup_files_on_exit"]: clean_files() + print("{} {} {} - {} {} {} - {} {}".format( + len(debug_data["loader_shellcode"]), + len(debug_data["payload_shellcode"]), + len(debug_data["final_shellcode"]), + len(debug_data["asm_initial"]), + len(debug_data["asm_cleanup"]), + len(debug_data["asm_fixup"]), + len(debug_data["original_exe"]), + len(debug_data["infected_exe"]), + )) + if __name__ == "__main__": main() \ No newline at end of file