问题
I have a requirement to hide a process in Task Manager. It is for Intranet scenario. So, everything is legitimate. :)
Please feel free to share any code you have (preferably in C#) or any other techniques or any issues in going with this route.
Update1: Most of the users have admin privileges in order to run some legacy apps. So, one of the suggestion was to hide it in task manager. If there are other approaches to prevent users from killing the process, that would be great.
Update2: Removing the reference to rootkit. Somehow made this post look negative.
回答1:
There is no supported way to accomplish this. The process list can be read at any privilege level. If you were hoping to hide a process from even Administrators, then this is doubly unsupported.
To get this to work, you would need to write a kernel mode rootkit to intercept calls to NtQuerySystemInformation so that the SystemProcessInformation info class fails to list your hidden process.
Intercepting system calls is very difficult to do safely, and the 64 bit Windows kernels go out of their way to prevent this from being possible: trying to modify the syscall table results in an instant blue screen. It's going to be very difficult on those platforms
Here is an example of a rootkit that tries to do something similar (and has several serious problems).
回答2:
Don't try to stop it from being killed - you're not going to manage it. Instead, make it regularly call home to a webservice. When the webservice notices a client "going silent" it can ping the machine to see if it's just a reboot issue, and send an email to a manager (or whoever) to discipline whoever has killed the process.
回答3:
If you want to prevent users from killing the process from task manager, you can just use a security descriptor on the process to deny terminate access to everyone. Administrators technically can still kill the process by taking ownership of the process and resetting the DACL, but there is no interface to do either of these things from Task Manager. Process Explorer may have an interface to though.
When your process starts, use SetKernelObjectSecurity with DACL_SECURITY_INFORMATION
using the current process handle. Set a DACL with zero ACLs. This will deny all access to everyone, including those trying to end your process with task manager.
Here is an example that also changes the process's owner:
SECURITY_DESCRIPTOR sd;
ACL dacl;
SID_IDENTIFIER_AUTHORITY ntauth = SECURITY_NT_AUTHORITY;
PSID owner;
assert(InitializeAcl(&dacl, sizeof dacl, ACL_REVISION));
assert(AllocateAndInitializeSid(&ntauth, 1, SECURITY_LOCAL_SYSTEM_RID, 0,0,0,0,0,0,0, &owner));
assert(InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION));
assert(SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE));
assert(SetSecurityDescriptorOwner(&sd, owner, FALSE));
assert(SetKernelObjectSecurity(GetCurrentProcess(), DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, &sd));
assert(FreeSid(owner) == NULL);
Unfortunately, it doesn't seem to be effective. I can still close the process (although not as a limited user). Perhaps Task Manager is taking ownership or invoking some other privilege to kill the process? I seem to remember this working in previous versions of Windows (I was testing 2003), but I could be mistaken.
回答4:
I hope that you would not be able to.
Update: given the scenario, I think that you will probably be best off running it under a different admin account. That may help alert people to the fact that they should not kill the process.
回答5:
Alternatively, you could write a small "checker" utility that checks if the app is running, if it isn't it automatically starts it. Then add code to the app to check for the "checker" utility that does the same. This way if one is terminated, then the other starts it back up. I've seem virus's do this, and it seems to work pretty effectively.
回答6:
Write a driver - You can use ObRegisterCallbacks to register process object access notification. Return STATUS_ACCESS_DENIED when DesiredAccess contains access rights you don't like, like process termination, or writing to process memory.
http://msdn.microsoft.com/en-us/library/windows/hardware/ff558692(v=vs.85).aspx
回答7:
If you simply need to disguise the process and not hide it completely, you can rename it winlogon.exe or svchost.exe and it will likely be ignored by users. But as Sergio mentioned, that's security by obscurity and it's got a bad reputation for a reason.
Preventing users from killing a process is another difficulty if they have proper privileges. The only method I know is to have multiple processes that watch each other and restart any watched process which gets killed. Again, this is going down a shady path.
回答8:
There is no easy or supported way to do this. Even if you wrote a rootkit to do it then that could very easily get broken by a future update that was made to plug that hole. I would reexamine whether that is something you want to do.
回答9:
As people mentioned above, the best method is 2 tasks, monitoring each other, I understand you don't want to waste CPU, so the best way is to establish an event between the tasks which will be triggered when one closes.
I am not entirely sure on how to set up the hook, but then you don't use a while loop which does waste CPU.
回答10:
Not sure why this hasnt been suggested yet but heres my first answer on this site. instead of preventing the user from killing a process. (Requires rootkit hooking.) You can simply disable the task manager from being used with a registry input.
public static void ToggleTaskManager(bool toggle)
{
Microsoft.Win32.RegistryKey HKCU = Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey key = HKCU.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System");
key.SetValue("DisableTaskMgr", toggle ? 0 : 1, Microsoft.Win32.RegistryValueKind.DWord);
}
回答11:
Have you looked at writing a service? This way the service runs as the local system, and the application runs under the user's context, and the service can ensure that things are still done as needed, and the application is just an interface to this service. Killing the application would just result in the user not seeing any notices, system tray icon, etc, but the service is still doing its job.
回答12:
Many people might know how to do it, but just won't post it here. It is very dangerous to post malicious code on the internet. Who knows you might be in danger. Ask some computer engineer. I'll give you the structure of the program though.
Just inject your program's dll into explorer.exe.
Your process won't just show up because it is not running as a program but is being ran in a program (explorer.exe). The user just won't see the process even if he uses any kind of task manager.
回答13:
What about you just ask the user to don't kill the process ? How much time you'll spend doing it, for a behavior that is clearly childish from employees in the same company.
回答14:
I saw @Chris Smith answer and I decided to convert it to C#.
Here is the code, taken from here, for a simple Winform application:
C# variation:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Hide2
{
public partial class Form1 : Form
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetKernelObjectSecurity(IntPtr Handle, int securityInformation, [Out] byte[] pSecurityDescriptor,
uint nLength, out uint lpnLengthNeeded);
public static RawSecurityDescriptor GetProcessSecurityDescriptor(IntPtr processHandle)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] psd = new byte[0];
uint bufSizeNeeded;
// Call with 0 size to obtain the actual size needed in bufSizeNeeded
GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, psd, 0, out bufSizeNeeded);
if (bufSizeNeeded < 0 || bufSizeNeeded > short.MaxValue)
throw new Win32Exception();
// Allocate the required bytes and obtain the DACL
if (!GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION,
psd = new byte[bufSizeNeeded], bufSizeNeeded, out bufSizeNeeded))
throw new Win32Exception();
// Use the RawSecurityDescriptor class from System.Security.AccessControl to parse the bytes:
return new RawSecurityDescriptor(psd, 0);
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetKernelObjectSecurity(IntPtr Handle, int securityInformation, [In] byte[] pSecurityDescriptor);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
[Flags]
public enum ProcessAccessRights
{
PROCESS_CREATE_PROCESS = 0x0080, // Required to create a process.
PROCESS_CREATE_THREAD = 0x0002, // Required to create a thread.
PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000: This access right is not supported.
PROCESS_SET_INFORMATION = 0x0200, // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
PROCESS_SET_QUOTA = 0x0100, // Required to set memory limits using SetProcessWorkingSetSize.
PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
PROCESS_TERMINATE = 0x0001, // Required to terminate a process using TerminateProcess.
PROCESS_VM_OPERATION = 0x0008, // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
DELETE = 0x00010000, // Required to delete the object.
READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
SYNCHRONIZE = 0x00100000, // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
WRITE_DAC = 0x00040000, // Required to modify the DACL in the security descriptor for the object.
WRITE_OWNER = 0x00080000, // Required to change the owner in the security descriptor for the object.
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF),// All possible access rights for a process object.
}
public static void SetProcessSecurityDescriptor(IntPtr processHandle, RawSecurityDescriptor dacl)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] rawsd = new byte[dacl.BinaryLength];
dacl.GetBinaryForm(rawsd, 0);
if (!SetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, rawsd))
throw new Win32Exception();
}
public Form1()
{
InitializeComponent();
// Get the current process handle
IntPtr hProcess = GetCurrentProcess();
// Read the DACL
var dacl = GetProcessSecurityDescriptor(hProcess);
// Insert the new ACE
dacl.DiscretionaryAcl.InsertAce(
0,
new CommonAce(
AceFlags.None,
AceQualifier.AccessDenied,
(int)ProcessAccessRights.PROCESS_ALL_ACCESS,
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
false,
null)
);
// Save the DACL
SetProcessSecurityDescriptor(hProcess, dacl);
}
}
}
After running it as a limited user, I can't kill it from the task manager, only as administrator.
I left the X
button to be able to close it without an admin but it also possible to remove it.
The result:
Powershell variation:
$source = @"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
namespace Hide2
{
public class myForm
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetKernelObjectSecurity(IntPtr Handle, int securityInformation, [Out] byte[] pSecurityDescriptor,
uint nLength, out uint lpnLengthNeeded);
public static RawSecurityDescriptor GetProcessSecurityDescriptor(IntPtr processHandle)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] psd = new byte[0];
uint bufSizeNeeded;
// Call with 0 size to obtain the actual size needed in bufSizeNeeded
GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, psd, 0, out bufSizeNeeded);
if (bufSizeNeeded < 0 || bufSizeNeeded > short.MaxValue)
throw new Win32Exception();
// Allocate the required bytes and obtain the DACL
if (!GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION,
psd = new byte[bufSizeNeeded], bufSizeNeeded, out bufSizeNeeded))
throw new Win32Exception();
// Use the RawSecurityDescriptor class from System.Security.AccessControl to parse the bytes:
return new RawSecurityDescriptor(psd, 0);
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetKernelObjectSecurity(IntPtr Handle, int securityInformation, [In] byte[] pSecurityDescriptor);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
[Flags]
public enum ProcessAccessRights
{
PROCESS_CREATE_PROCESS = 0x0080, // Required to create a process.
PROCESS_CREATE_THREAD = 0x0002, // Required to create a thread.
PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000: This access right is not supported.
PROCESS_SET_INFORMATION = 0x0200, // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
PROCESS_SET_QUOTA = 0x0100, // Required to set memory limits using SetProcessWorkingSetSize.
PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
PROCESS_TERMINATE = 0x0001, // Required to terminate a process using TerminateProcess.
PROCESS_VM_OPERATION = 0x0008, // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
DELETE = 0x00010000, // Required to delete the object.
READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
SYNCHRONIZE = 0x00100000, // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
WRITE_DAC = 0x00040000, // Required to modify the DACL in the security descriptor for the object.
WRITE_OWNER = 0x00080000, // Required to change the owner in the security descriptor for the object.
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF),// All possible access rights for a process object.
}
public static void SetProcessSecurityDescriptor(IntPtr processHandle, RawSecurityDescriptor dacl)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] rawsd = new byte[dacl.BinaryLength];
dacl.GetBinaryForm(rawsd, 0);
if (!SetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, rawsd))
throw new Win32Exception();
}
public static void ProtectMyProcess()
{
// Get the current process handle
IntPtr hProcess = GetCurrentProcess();
// Read the DACL
var dacl = GetProcessSecurityDescriptor(hProcess);
// Insert the new ACE
dacl.DiscretionaryAcl.InsertAce(
0,
new CommonAce(
AceFlags.None,
AceQualifier.AccessDenied,
(int)ProcessAccessRights.PROCESS_ALL_ACCESS,
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
false,
null)
);
// Save the DACL
SetProcessSecurityDescriptor(hProcess, dacl);
}
}
}
"@
Add-Type -TypeDefinition $Source -Language CSharp
[ScriptBlock]$scriptNewForm = {
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Form.Text = "PowerShell form"
$Form.TopMost = $true
$Form.Width = 303
$Form.Height = 274
[void]$Form.ShowDialog()
$Form.Dispose()
}
$SleepTimer = 200
$MaxResultTime = 120
$MaxThreads = 3
$ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host)
$RunspacePool.Open()
$Jobs = @()
$PowershellThread = [powershell]::Create().AddScript($scriptNewForm)
$PowershellThread.RunspacePool = $RunspacePool
$Handle = $PowershellThread.BeginInvoke()
$Job = "" | Select-Object Handle, Thread, object
$Job.Handle = $Handle
$Job.Thread = $PowershellThread
$Job.Object = $computer
$Jobs += $Job
[Hide2.myForm]::ProtectMyProcess()
<#
ForEach ($Job in $Jobs){
$Job.Thread.EndInvoke($Job.Handle)
$Job.Thread.Dispose()
$Job.Thread = $Null
$Job.Handle = $Null
}
#>
回答15:
to stop the process from being permanently killed, have the first thing the process does is to call 'atexit()' and have the atexit() function start the process
回答16:
I know this question is old but I answered a duplicate question a while ago that contains some nice information there that isn't here so I thought I'd link to it. See My answer to the duplicate question. Also if your true goal is to stop the users from killing the process then what I know used to work very easily, although it is a bit hackish and I don't know if this still works, is to simply name your application lsass.exe and task manager will not allow even an admin user to close the process. for this method it doesn't matter which user started the process or where the executable resides on the file system, it seems windows just checks to see if the process is named this then do not allow it to be ended.
Update: I just tried to do the lsass.exe trick on windows 7 and it appears to have been fixed, but my guess is it still works on windows xp and maybe even the earlier service packs of the versions beyond xp. Even though this no longer works at the time of this writing I thought I'd include it anyway as a fun fact.
来源:https://stackoverflow.com/questions/187983/how-do-i-hide-a-process-in-task-manager-in-c