Update InflativeLoading.py

This commit is contained in:
Winslow
2024-02-16 15:29:42 -05:00
committed by GitHub
parent 460c58515c
commit 4e56f35520
+111 -93
View File
@@ -1,26 +1,49 @@
import ctypes, struct
from keystone import *
import sys
import argparse
def print_banner():
banner="""
██╗███╗ ██╗███████╗██╗ █████╗ ████████╗██╗██╗ ██╗███████╗
██║████╗ ██║██╔════╝██║ ██╔══██╗╚══██╔══╝██║██║ ██║██╔════╝
██║██╔██╗ ██║█████╗ ██║ ███████║ ██║ ██║██║ ██║█████╗
██║██║╚██╗██║██╔══╝ ██║ ██╔══██║ ██║ ██║╚██╗ ██╔╝██╔══╝
██║██║ ╚████║██║ ███████╗██║ ██║ ██║ ██║ ╚████╔╝ ███████╗
╚═╝╚═╝ ╚═══╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝
██╗ ██████╗ █████╗ ██████╗ ██╗███╗ ██╗ ██████╗
██║ ██╔═══██╗██╔══██╗██╔══██╗██║████╗ ██║██╔════╝
██║ ██║ ██║███████║██║ ██║██║██╔██╗ ██║██║ ███╗
██║ ██║ ██║██╔══██║██║ ██║██║██║╚██╗██║██║ ██║
███████╗╚██████╔╝██║ ██║██████╔╝██║██║ ╚████║╚██████╔╝
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝
"""
print(banner)
print("Author: Senzee")
print("Github Repository: https://github.com/senzee1984/InflativeLoading")
print("Twitter: senzee@1984")
print("Website: https://senzee.net")
print("Description: Dynamically convert a native PE to PIC shellcode")
print("Attention: Bugs are expected, more support and improvements are coming!\n\n\n")
def generate_assembly_instructions(new_cmd):
def generate_asm_by_cmdline(new_cmd):
new_cmd_length = len(new_cmd) * 2 + 12
unicode_cmd = [ord(c) for c in new_cmd]
fixed_instructions = [
"mov rsi, [rax + 0x20]; # RSI = Address of ProcessParameter",
"add rsi, 0x70; # RSI points to CommandLine member",
"mov rsi, [rax + 0x20]; # RSI = Address of ProcessParameter",
"add rsi, 0x70; # RSI points to CommandLine member",
f"mov byte ptr [rsi], {new_cmd_length}; # Set Length to the length of new commandline",
"mov byte ptr [rsi+2], 0xff; # Set the max length of cmdline to 0xff bytes",
"mov rsi, [rsi+8]; # RSI points to the string",
"mov dword ptr [rsi], 0x002e0031; # Push '.1'",
"mov dword ptr [rsi+0x4], 0x00780065; # Push 'xe'",
"mov dword ptr [rsi+0x8], 0x00200065; # Push ' e'"
"mov dword ptr [rsi], 0x002e0031; # Push '.1'",
"mov dword ptr [rsi+0x4], 0x00780065; # Push 'xe'",
"mov dword ptr [rsi+0x8], 0x00200065; # Push ' e'"
]
start_offset = 0xC
dynamic_instructions = []
for i, char in enumerate(unicode_cmd):
@@ -32,41 +55,45 @@ def generate_assembly_instructions(new_cmd):
dword = hex_char + dword
instruction = f"mov dword ptr [rsi+0x{offset-2:x}], 0x{dword};"
dynamic_instructions.append(instruction)
if len(unicode_cmd) % 2 != 0:
instruction = f"mov word ptr [rsi+0x{offset:x}], 0x{dword};"
dynamic_instructions.append(instruction)
final_offset = start_offset + len(unicode_cmd) * 2
dynamic_instructions.append(f"mov byte ptr [rsi+0x{final_offset:x}], 0;")
instructions = fixed_instructions + dynamic_instructions
return "\n".join(instructions)
def read_pe_file(file_path):
def read_dump_file(file_path):
with open(file_path, 'rb') as file:
return bytearray(file.read())
def print_byte_array(byte_array, line_length=20, max_lines=20):
for i in range(min(max_lines, len(byte_array) // line_length)):
line = byte_array[i * line_length:(i + 1) * line_length]
def print_shellcode(sc):
for i in range(min(20, len(sc))):
line = sc[i * 20:(i + 1) * 20]
formatted_line = ''.join([f"\\x{b:02x}" for b in line])
print(f"buf += b\"{formatted_line}\"")
print("......"+str(len(byte_array)-400) +" more bytes......")
print("......"+str(len(sc)-400) +" more bytes......")
if __name__ == "__main__":
if len(sys.argv)!=3:
print("Usage: python3 shellcodify.py calc.bin \"sekurlsa::logonpasswords\"")
pe_file_path = sys.argv[1]
pe_array = read_pe_file(pe_file_path)
new_cmd = sys.argv[2]
update_cmdline_asm = generate_assembly_instructions(new_cmd)
print_banner()
parser = argparse.ArgumentParser(description='Dynamically generate shellcode stub to append to the dump file')
parser.add_argument('--bin', '-b', required=True, dest='bin',help='The binary file dumped by DumpPEFromMemory.exe')
parser.add_argument('--cmdline', '-c', required=False, default="", dest='cmdline',help='Supplied command line')
parser.add_argument('--output', '-o', required=True, dest='output',help='Save the PIC code as a bin file')
parser.add_argument('--execution', '-e', required=False, default='False', dest='sc_exec',help='(Only Windows) Immediately execute shellcoded PE? True/False')
args = parser.parse_args()
bin= args.bin
cmdline = args.cmdline
output = args.output
sc_exec = args.sc_exec
pe_array = read_dump_file(bin)
update_cmdline_asm = generate_asm_by_cmdline(cmdline) # Generate shellcode that used to update command line
CODE = (
@@ -83,8 +110,8 @@ f"{update_cmdline_asm}"
"find_kernel32:"
" mov rsi,[rax+0x18];" # RSI = Address of _PEB_LDR_DATA
" mov rsi,[rsi + 0x30];" # RSI = Address of the InInitializationOrderModuleList
" mov r9, [rsi];" # python.exe
" mov r9, [r9];" # ntdll.dll
" mov r9, [rsi];"
" mov r9, [r9];"
" mov r9, [r9+0x10];" # kernel32.dll
" jmp function_stub;" # Jump to func call stub
@@ -141,7 +168,7 @@ f"{update_cmdline_asm}"
" ret;"
"function_stub:" # Achieve PIC and elminiate 0x00 byte
"function_stub:"
" mov rbp, r9;" # RBP stores base address of Kernel32.dll
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" call parse_module;" # Search LoadLibraryA's address
@@ -158,20 +185,20 @@ f"{update_cmdline_asm}"
CODE_OFFSET = 4096 - CODE_LEN
CODE2 = (
" jmp fix_iat;" # Jump to fix_iat section
" jmp fix_import_dir;" # Jump to fix_iat section
"find_nt_header:"
"find_nt_header:" # Quickly return NT header in RAX
" xor rax, rax;"
" mov eax, [rbx+0x3c];" # EAX contains e_lfanew
" add rax, rbx;" # RAX points to NT Header
" ret;" # Return
" ret;"
"fix_iat:" # Init necessary variable for fixing IAT
"fix_import_dir:" # Init necessary variable for fixing IAT
" xor rsi, rsi;"
" xor rdi, rdi;"
f"lea rbx, [rip+{CODE_OFFSET}];"
f"lea rbx, [rip+{CODE_OFFSET}];" # Jump to the dump file
" call find_nt_header;"
" mov esi, [rax+0x90];" # ESI = ImportDir RVA
" add rsi, rbx;" # RSI points to ImportDir
@@ -179,13 +206,13 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" add rdi, rsi;" # RDI = ImportDir VA + Size
"loop_imported_module:"
"loop_module:"
" cmp rsi, rdi;" # Compare current descriptor with the end of import directory
" je module_loop_end;" # If equal, exit the loop
" je loop_end;" # If equal, exit the loop
" xor rdx ,rdx;"
" mov edx, [rsi];" # EDX = ILT RVA (32-bit)
" test rdx, rdx;" # Check if ILT RVA is zero (end of descriptors)
" je module_loop_end;" # If zero, exit the loop
" je loop_end;" # If zero, exit the loop
" xor rcx, rcx;"
" mov ecx, [rsi+0xc];" # RCX = Module Name RVA
" add rcx, rbx;" # RCX points to Module Name
@@ -201,12 +228,12 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" mov r15, r8;" # Backup IAT Address
"loop_imported_func:"
"loop_func:"
" mov rdx, r14;" # Restore ILT address + processed entries
" mov r8, r15;" # Restore IAT Address + processed entries
" mov rdx, [rdx];" # RDX = Ordinal or RVA of HintName Table
" test rdx, rdx;" # Check if it's the end of the ILT/IAT
" je next_descriptor;" # If zero, move to the next descriptor
" je next_module;" # If zero, move to the next descriptor
" mov r9, 0x8000000000000000;"
" test rdx, r9;" # Check if it is import by ordinal (highest bit set)
" mov rbp, rcx;" # Save module base address
@@ -232,20 +259,20 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" mov [r8], rax;" # Write the resolved address to the IAT
" add r15, 0x8;" # Move to the next IAT entry (64-bit addresses)
" add r14, 0x8;" # Movce to the next ILT entry
" jmp loop_imported_func;" # Repeat for the next function
" jmp loop_func;" # Repeat for the next function
"next_descriptor:"
"next_module:"
" add rsi, 0x14;" # Move to next import descriptor
" jmp loop_imported_module;" # Continue loop
" jmp loop_module;" # Continue loop
"module_loop_end:"
"loop_end:"
"fix_reloc:" # Save RBX //dq rbx+21b0 l46
"fix_basereloc_dir:" # Save RBX //dq rbx+21b0 l46
" xor rsi, rsi;"
" xor rdi, rdi;"
" xor r8, r8;" # Empty R8 to save page RVA
@@ -268,9 +295,9 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" xor rcx, rcx;"
"loop_reloc_block:"
"loop_block:"
" cmp rsi, rdi;" # Compare current block with the end of BaseReloc
" jge reloc_fixed_end;" # If equal, exit the loop
" jge basereloc_fixed_end;" # If equal, exit the loop
" xor r8, r8;"
" mov r8d, [rsi];" # R8 = Current block's page RVA
" add r8, rbx;" # R8 points to current block page (Should add an offset later)
@@ -283,7 +310,7 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" sub rdx, 8;" # RDX = End of all entries in current block
"loop_reloc_entries:"
"loop_entries:"
" cmp rsi, rdx;" # If we reached the end of current block
" jz next_block;" # Move to next block
" xor rax, rax;"
@@ -299,7 +326,7 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" sub [r8], r14;" # Update the address
" mov r8, r11;" # Restore r8
" add rsi, 2;" # Move to next entry by adding 2 bytes
" jmp loop_reloc_entries;"
" jmp loop_entries;"
"skip_padding_entry:" # If the last entry is a padding entry
@@ -307,28 +334,28 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
"next_block:"
" jmp loop_reloc_block;"
" jmp loop_block;"
"reloc_fixed_end:"
"basereloc_fixed_end:"
" sub rsp, 0x8;" # Stack alignment
"fix_delayed_iat:"
"fix_delayed_import_dir:"
" call find_nt_header;"
" mov esi, [rax+0xf0];" # ESI = DelayedImportDir RVA
" test esi, esi;" # If RVA = 0?
" jz delayed_module_loop_end;" # Skip delay import table fix
" jz delayed_loop_end;" # Skip delay import table fix
" add rsi, rbx;" # RSI points to DelayedImportDir
"loop_delayed_module:"
"delayed_loop_module:"
" xor rcx, rcx;"
" mov ecx, [rsi+4];" # RCX = Module name string RVA
" test rcx, rcx;" # If RVA = 0, then all modules are processed
" jz delayed_module_loop_end;" # Exit the module loop
" jz delayed_loop_end;" # Exit the module loop
" add rcx, rbx;" # RCX = Module name
" call r12;" # Call LoadLibraryA
" mov rcx, rax;" # Module handle for GetProcAddress for 1st arg
@@ -342,46 +369,46 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
" mov r15, r8;" # Backup IAT Address
"loop_delayed_imported_function:"
"delayed_loop_func:"
" mov rdx, r14;" # Restore INT Address + processed data
" mov r8, r15;" # Restore IAT Address + processed data
" mov rdx, [rdx];" # RDX = Name Address RVA
" test rdx, rdx;" # If Name Address value is 0, then all functions are fixed
" jz next_delayed_module;" # Process next module
" jz delayed_next_module;" # Process next module
" mov r9, 0x8000000000000000;"
" test rdx, r9;" # Check if it is import by ordinal (highest bit set of NameAddress)
" mov rbp, rcx;" # Save module base address
" jnz resolve_delayed_by_ordinal;" # If set, resolve by ordinal
" jnz delayed_resolve_by_ordinal;" # If set, resolve by ordinal
"resolve_delayed_by_name:"
"delayed_resolve_by_name:"
" add rdx, rbx;" # RDX points to NameAddress Table
" add rdx, 2;" # RDX points to Function Name
" call r13;" # Call GetProcAddress
" jmp update_delayed_iat;" # Go to update IAT
" jmp delayed_update_iat;" # Go to update IAT
"resolve_delayed_by_ordinal:"
"delayed_resolve_by_ordinal:"
" mov r9, 0x7fffffffffffffff;"
" and rdx, r9;" # RDX = Ordinal number
" call r13;" # Call GetProcAddress with ordinal
"update_delayed_iat:"
"delayed_update_iat:"
" mov rcx, rbp;" # Restore module base address
" mov r8, r15;" # Restore current IAT address + processed
" mov [r8], rax;" # Write the resolved address to the IAT
" add r15, 0x8;" # Move to the next IAT entry (64-bit addresses)
" add r14, 0x8;" # Movce to the next INT entry
" jmp loop_delayed_imported_function;" # Repeat for the next function
" jmp delayed_loop_func;" # Repeat for the next function
"next_delayed_module:"
"delayed_next_module:"
" add rsi, 0x20;" # Move to next delayed imported module
" jmp loop_delayed_module;" # Continue loop
" jmp delayed_loop_module;" # Continue loop
"delayed_module_loop_end:"
"delayed_loop_end:"
"all_completed:"
@@ -396,41 +423,32 @@ f"lea rbx, [rip+{CODE_OFFSET}];"
encoding2, count2 = ks.asm(CODE2)
encoding = encoding + encoding2
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
print("Shellcode Stub size: "+str(len(shellcode))+" bytes")
new_shellcode = shellcode + b"\x90"*(0x1000-len(shellcode)) + pe_array
print("Shellcoded PE's size: "+str(len(new_shellcode))+" bytes")
print("[+] Shellcode Stub size: "+str(len(shellcode))+" bytes")
print("[*] Padded to 0x1000 bytes to align with page boundary")
merged_shellcode = shellcode + b"\x90"*(0x1000-len(shellcode)) + pe_array
print("[!] Shellcoded PE's size: "+str(len(merged_shellcode))+" bytes\n\n")
print_shellcode(merged_shellcode)
print_byte_array(new_shellcode)
try:
with open(output, 'wb') as f:
f.write(merged_shellcode)
print("\n\nGenerated shellcode successfully saved in file "+output)
except Exception as e:
print(e)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(new_shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(new_shellcode)).from_buffer(new_shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(new_shellcode)))
print("Shellcode located at address %s" % hex(ptr))
input("CONTINUE TO EXECUTE SHELLCODE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_uint64(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
if sc_exec.lower() == "true":
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(merged_shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))
buf = (ctypes.c_char * len(merged_shellcode)).from_buffer(merged_shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(merged_shellcode)))
print("\n\n[#] Shellcode located at address %s" % hex(ptr))
input("\n[!] PRESS TO EXECUTE SHELLCODED EXE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint64(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))