Add support for passing the shellcode base address to the module export

Some cleanup and additional notes to the README
This commit is contained in:
Nick Landers
2022-05-09 16:57:34 -06:00
parent 5690685aee
commit e32abbe82e
9 changed files with 186 additions and 104 deletions
+28 -20
View File
File diff suppressed because one or more lines are too long
+30 -19
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7 -3
View File
@@ -10,10 +10,11 @@ def main():
parser.add_argument('-f', '--function-name', dest='function_name', help='The function to call after DllMain', default='SayHello')
parser.add_argument('-u', '--user-data', dest='user_data', help='Data to pass to the target function', default='dave')
parser.add_argument('-c', '--clear-header', dest='clear_header', action='store_true', help='Clear the PE header on load')
parser.add_argument('-b', '--pass-shellcode-base', dest='pass_shellcode_base', action='store_true', help='Pass shellcode base address to exported function')
parser.add_argument('-i', '--obfuscate-imports', dest='obfuscate_imports', action='store_true', help='Randomize import dependency load order', default=False)
parser.add_argument('-d', '--import-delay', dest='import_delay', help='Number of seconds to pause between loading imports', type=int, default=0)
parser.add_argument('-of', '--output-format', dest='output_format', help='Output format of the shellcode (e.g. raw,string)', type=str, default="raw")
arguments = parser.parse_args()
input_dll = arguments.input_dll
@@ -29,13 +30,16 @@ def main():
if arguments.obfuscate_imports:
flags = flags | 0x4 | arguments.import_delay << 16
if arguments.pass_shellcode_base:
flags |= 0x8
converted_dll = ConvertToShellcode(dll, HashFunctionName(arguments.function_name), arguments.user_data.encode(), flags)
if arguments.output_format=="raw":
print('Creating Shellcode: {}'.format(output_bin))
with open(output_bin, 'wb') as f:
f.write(converted_dll)
elif arguments.output_format=="string":
output_bin = input_dll.replace('.dll', '.txt')
converted_dll_text ="".join([r"\x{}".format(str(format(c,'02x'))) for c in converted_dll])
-5
View File
@@ -28,11 +28,6 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="ConvertToShellcode.py" />
<Compile Include="EncodeBlobs.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="pefile.py" />
<Compile Include="peutils.py" />
<Compile Include="ShellcodeRDI.py" />
</ItemGroup>
<!-- Uncomment the CoreCompile target to enable the Build command in
+25 -18
View File
File diff suppressed because one or more lines are too long
+22 -1
View File
@@ -52,7 +52,7 @@ The PE loader code uses `flags` argument to control the various options of loadi
- `SRDI_CLEARHEADER` [0x1]: The DOS Header and DOS Stub for the target DLL are completley wiped with null bytes on load (Except for e_lfanew). This might cause issues with stock windows APIs when supplying the base address as a psuedo `HMODULE`.
- `SRDI_CLEARMEMORY` [0x2]: After calling functions in the loaded module (`DllMain` and any exports), the DLL data will be cleared from memory. This is dangerous if you expect to continue executing code out of the module (Threads / `GetProcAddressR`).
- `SRDI_OBFUSCATEIMPORTS` [0x4]: The order of imports in the module will be randomized before starting IAT patching. Additionally, the high 16 bits of the flag can be used to store the number of seconds to pause before processing the next import. For example, `flags | (3 << 16)` will pause 3 seconds between every import.
- `SRDI_PASS_SHELLCODE_BASE` [0x8]: As opposed to passing supplied user data to the exported function, sRDI will instead pass the base address of the currently executing shellcode block. This can be useful for self-cleanup inside more advanced modules.
## Building
This project is built using Visual Studio 2019 (v142) and Windows SDK 10. The python script is written using Python 3.
@@ -67,6 +67,27 @@ After building the project, the other binaries will be located at:
- `bin\TestDLL_<arch>.dll`
- `bin\ShellcodeRDI_<arch>.bin`
If you would like to update the static blobs inside any of the tools:
```
> python .\lib\Python\EncodeBlobs.py -h
usage: EncodeBlobs.py [-h] solution_dir
sRDI Blob Encoder
positional arguments:
solution_dir Solution Directory
optional arguments:
-h, --help show this help message and exit
> python lib\Python\EncodeBlobs.py C:\code\srdi
[+] Updated C:\code\srdi\Native/Loader.cpp
[+] Updated C:\code\srdi\DotNet/Program.cs
[+] Updated C:\code\srdi\Python/ShellcodeRDI.py
[+] Updated C:\code\srdi\PowerShell/ConvertTo-Shellcode.ps1
```
## Alternatives
If you find my code disgusting, or just looking for an alternative memory-PE loader project, check out some of these:
+9 -2
View File
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.16
# Visual Studio Version 16
VisualStudioVersion = 16.0.31410.357
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestDLL", "TestDLL\TestDLL.vcxproj", "{558D08E4-48B4-4E5F-94E5-5783CF0557C4}"
EndProject
@@ -10,6 +10,10 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShellcodeRDI", "ShellcodeRDI\ShellcodeRDI.vcxproj", "{6FC09BDB-365F-4691-BBD9-CB7F69C9527A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Native", "Native\Native.vcxproj", "{68293519-3053-4AB6-921F-9690E2E1487F}"
ProjectSection(ProjectDependencies) = postProject
{6FC09BDB-365F-4691-BBD9-CB7F69C9527A} = {6FC09BDB-365F-4691-BBD9-CB7F69C9527A}
{558D08E4-48B4-4E5F-94E5-5783CF0557C4} = {558D08E4-48B4-4E5F-94E5-5783CF0557C4}
EndProjectSection
EndProject
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Python", "Python\Python.pyproj", "{BE642266-F34D-43C3-B6E4-EEBF8E489519}"
EndProject
@@ -78,4 +82,7 @@ Global
{68293519-3053-4AB6-921F-9690E2E1487F} = {F602BD8E-D2C2-4B04-85C6-292388CF1D83}
{BE642266-F34D-43C3-B6E4-EEBF8E489519} = {F602BD8E-D2C2-4B04-85C6-292388CF1D83}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3C9908F0-8E60-451C-B039-CE1FD3FFB06A}
EndGlobalSection
EndGlobal
+25 -16
View File
@@ -11,6 +11,7 @@
#define SRDI_CLEARHEADER 0x1
#define SRDI_CLEARMEMORY 0x2
#define SRDI_OBFUSCATEIMPORTS 0x4
#define SRDI_PASS_SHELLCODE_BASE 0x8
#define DEREF( name )*(UINT_PTR *)(name)
#define DEREF_64( name )*(DWORD64 *)(name)
@@ -114,7 +115,7 @@ _wcslen(wchar_t* s) {
string.MaximumLength = string.Length; \
string.Buffer = (PCHAR)buffer
ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD nUserdataLen, DWORD flags)
ULONG_PTR LoadDLL(PBYTE pbModule, DWORD dwFunctionHash, LPVOID lpUserData, DWORD dwUserdataLen, PVOID pvShellcodeBase, DWORD dwFlags)
{
#pragma warning( push )
#pragma warning( disable : 4055 ) // Ignore cast warnings
@@ -150,7 +151,9 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
PIMAGE_BASE_RELOCATION relocation;
PIMAGE_RELOC relocList;
PIMAGE_EXPORT_DIRECTORY exportDir;
#ifdef _WIN64
PIMAGE_RUNTIME_FUNCTION_ENTRY rfEntry;
#endif
PDWORD expName;
PWORD expOrdinal;
LPCSTR expNameStr;
@@ -255,7 +258,7 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
// STEP 2: load our image into a new permanent location in memory
///
ntHeaders = RVA(PIMAGE_NT_HEADERS, dllData, ((PIMAGE_DOS_HEADER)dllData)->e_lfanew);
ntHeaders = RVA(PIMAGE_NT_HEADERS, pbModule, ((PIMAGE_DOS_HEADER)pbModule)->e_lfanew);
// Perform sanity checks on the image (Stolen from https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c)
@@ -310,16 +313,16 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
// Copy over the headers
if (flags & SRDI_CLEARHEADER) {
((PIMAGE_DOS_HEADER)baseAddress)->e_lfanew = ((PIMAGE_DOS_HEADER)dllData)->e_lfanew;
if (dwFlags & SRDI_CLEARHEADER) {
((PIMAGE_DOS_HEADER)baseAddress)->e_lfanew = ((PIMAGE_DOS_HEADER)pbModule)->e_lfanew;
for (i = ((PIMAGE_DOS_HEADER)dllData)->e_lfanew; i < ntHeaders->OptionalHeader.SizeOfHeaders; i++) {
((PBYTE)baseAddress)[i] = ((PBYTE)dllData)[i];
for (i = ((PIMAGE_DOS_HEADER)pbModule)->e_lfanew; i < ntHeaders->OptionalHeader.SizeOfHeaders; i++) {
((PBYTE)baseAddress)[i] = ((PBYTE)pbModule)[i];
}
}else{
for (i = 0; i < ntHeaders->OptionalHeader.SizeOfHeaders; i++) {
((PBYTE)baseAddress)[i] = ((PBYTE)dllData)[i];
((PBYTE)baseAddress)[i] = ((PBYTE)pbModule)[i];
}
}
@@ -333,7 +336,7 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
for (i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++) {
for (c = 0; c < sectionHeader->SizeOfRawData; c++) {
((PBYTE)(baseAddress + sectionHeader->VirtualAddress))[c] = ((PBYTE)(dllData + sectionHeader->PointerToRawData))[c];
((PBYTE)(baseAddress + sectionHeader->VirtualAddress))[c] = ((PBYTE)(pbModule + sectionHeader->PointerToRawData))[c];
}
}
@@ -373,7 +376,7 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
///
dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
randSeed = (DWORD)((ULONGLONG)dllData);
randSeed = (DWORD)((ULONGLONG)pbModule);
if (dataDir->Size) {
@@ -384,8 +387,8 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
}
importDesc = RVA(PIMAGE_IMPORT_DESCRIPTOR, baseAddress, dataDir->VirtualAddress);
if (flags & SRDI_OBFUSCATEIMPORTS && importCount > 1) {
sleep = (flags & 0xFFFF0000);
if (dwFlags & SRDI_OBFUSCATEIMPORTS && importCount > 1) {
sleep = (dwFlags & 0xFFFF0000);
sleep = sleep >> 16;
for (i = 0; i < importCount - 1; i++) {
@@ -419,7 +422,7 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
}
}
if (flags & SRDI_OBFUSCATEIMPORTS && importCount > 1) {
if (sleep & dwFlags & SRDI_OBFUSCATEIMPORTS && importCount > 1) {
pSleep(sleep * 1000);
}
}
@@ -577,16 +580,22 @@ ULONG_PTR LoadDLL(PBYTE dllData, DWORD dwFunctionHash, LPVOID lpUserData, DWORD
if (dwFunctionHash == funcHash && expOrdinal)
{
exportFunc = RVA(EXPORTFUNC, baseAddress, *(PDWORD)(baseAddress + exportDir->AddressOfFunctions + (*expOrdinal * 4)));
exportFunc(lpUserData, nUserdataLen);
if (dwFlags & SRDI_PASS_SHELLCODE_BASE) {
exportFunc(pvShellcodeBase, sizeof(PVOID));
} else {
exportFunc(lpUserData, dwUserdataLen);
}
break;
}
}
} while (0);
}
if (flags & SRDI_CLEARMEMORY && pVirtualFree && pLocalFree) {
if (!pVirtualFree((LPVOID)dllData, 0, 0x8000))
pLocalFree((LPVOID)dllData);
if (dwFlags & SRDI_CLEARMEMORY && pVirtualFree && pLocalFree) {
if (!pVirtualFree((LPVOID)pbModule, 0, 0x8000))
pLocalFree((LPVOID)pbModule);
}
// Atempt to return a handle to the module