2008-10-09 619 views
31

Tengo un requisito para ocultar un proceso en el Administrador de tareas. Es para el escenario de Intranet. Entonces, todo es legítimo :)¿Cómo se oculta un proceso en el Administrador de tareas en C#?

No dude en compartir cualquier código que tenga (preferiblemente en C#) o cualquier otra técnica o cualquier problema con esta ruta.

Update1: la mayoría de los usuarios tienen privilegios de administrador para ejecutar algunas aplicaciones heredadas. Entonces, una de las sugerencias fue esconderlo en el administrador de tareas. Si hay otros enfoques para evitar que los usuarios maten el proceso, sería genial.

Update2: Eliminando la referencia a rootkit. De alguna manera, hizo que esta publicación se viera negativa.

+0

cómo se está en una intranet un escenario legítimo? No les des admin priveliges ... –

+0

Ese es el problema. La mayoría de los usuarios tienen privilegios de administrador para admitir algunas aplicaciones heredadas. –

+14

Si los usuarios tienen privilegios de administrador, POSEAN LA máquina, fin de la historia. –

Respuesta

42

No hay forma admitida de lograr esto. La lista de procesos se puede leer en cualquier nivel de privilegio. Si esperaba ocultar un proceso incluso a los Administradores, entonces esto es doblemente incompatible.

Para que esto funcione, necesitará escribir un rootkit en modo kernel para interceptar llamadas al NtQuerySystemInformation para que la clase de información SystemProcessInformation no pueda listar su proceso oculto.

La interceptación de llamadas al sistema es muy difícil de realizar de forma segura, y los kernels de Windows de 64 bits van a out of their way para evitar que esto sea posible: intentar modificar los resultados de la tabla syscall en una pantalla azul instantánea. Va a ser muy difícil en esas plataformas

Here es un ejemplo de un rootkit que intenta hacer algo similar (y tiene varios problemas graves).

10

Espero que no puedas.

Actualización: teniendo en cuenta el escenario, creo que probablemente sea mejor que lo ejecute con una cuenta de administrador diferente. Eso puede ayudar a alertar a las personas sobre el hecho de que no deberían matar el proceso.

+0

Entiendo tu punto. –

4

Si simplemente necesita ocultar el proceso y no ocultarlo por completo, puede cambiarle el nombre winlogon.exe o svchost.exe y es probable que los usuarios lo ignoren. Pero como mencionó Sergio, eso es seguridad por oscuridad y tiene una mala reputación por una razón.

Evitar que los usuarios maten un proceso es otra dificultad si tienen los privilegios adecuados. El único método que conozco es tener múltiples procesos que se miran unos a otros y reiniciar cualquier proceso observado que muera. De nuevo, esto va por un camino sombreado.

+0

Odio decirlo, pero como los usuarios tienen privilegios de administrador que disfrazan el proceso, es probablemente su mejor opción. Tenga cuidado, sin embargo: el software antivirus puede ver esto como un comportamiento malicioso y simplemente bloquear el programa. –

3

No existe una manera fácil o compatible para hacerlo. Incluso si escribió un rootkit para hacerlo, eso podría romperse fácilmente con una actualización futura que se hizo para tapar ese agujero. Volvería a examinar si eso es algo que quieres hacer.

64

No intente evitar que lo maten, no lo va a gestionar. En su lugar, haz que regularmente llame a casa a un servicio web. Cuando el servicio web nota que un cliente "está en silencio", puede hacer ping a la máquina para ver si se trata de un problema de reinicio, y enviar un correo electrónico a un administrador (o quien sea) para disciplinar a quien haya matado el proceso.

+4

primera buena respuesta que estaba buscando. –

+0

+100, inicialmente estaba buscando hacer lo que el autor original quería hacer, pero esto parece una excelente idea. – Zack

+0

Jon Skeet necesita más votos positivos. – pseudocoder

18

Si desea evitar que los usuarios eliminen el proceso del administrador de tareas, puede usar un descriptor de seguridad en el proceso para denegar el acceso a todos.Los administradores técnicamente aún pueden matar el proceso asumiendo la propiedad del proceso y reiniciando el DACL, pero no hay ninguna interfaz para hacer cualquiera de estas cosas desde el Administrador de tareas. Process Explorer aunque puede tener una interfaz.

Cuando se inicia el proceso, utilice SetKernelObjectSecurity con DACL_SECURITY_INFORMATION utilizando el controlador de proceso actual. Establezca una DACL con cero ACL. Esto negará todo acceso a todos, incluidos los que intentan finalizar su proceso con el administrador de tareas.

Aquí es un ejemplo que también cambia el dueño del proceso:

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); 

Por desgracia, no parece ser eficaz. Todavía puedo cerrar el proceso (aunque no como usuario limitado). ¿Tal vez el Administrador de tareas toma posesión o invoca algún otro privilegio para matar el proceso? Me parece recordar que esto funcionaba en versiones anteriores de Windows (estaba probando 2003), pero podría estar equivocado.

+0

+10. Esto definitivamente vale la pena mirar ... Muchas gracias. –

+0

He intentado hacer esto en una aplicación de muestra, pero no pareció tener ningún efecto (aunque todas las llamadas tuvieron éxito). Odio decirlo, pero ¿puedes vincularlo a algún código que haga esto? –

+0

Estaba a punto de hacer la misma pregunta. Gracias Stephen. –

6

Como alternativa, podría escribir una pequeña utilidad "checker" que verifique si la aplicación se está ejecutando, si no es así, la iniciará automáticamente. A continuación, agregue código a la aplicación para verificar la utilidad "checker" que hace lo mismo. De esta forma, si uno termina, el otro lo inicia nuevamente. Parece que los virus hacen esto, y parece funcionar bastante bien.

+0

sí. Es posible. quería evitar los ciclos de CPU para todo ese control. –

1

¿Qué tal si le pides al usuario que no mate el proceso? Cuánto tiempo pasará haciéndolo, por un comportamiento claramente infantil de los empleados de la misma empresa.

+0

El proceso está en marcha para educar a los usuarios. Tengo que cubrir esta posibilidad desde un ángulo técnico. –

3

Como se mencionó anteriormente, el mejor método es 2 tareas, supervisándose entre sí, Entiendo que no se quiere desperdiciar CPU, por lo que la mejor manera es establecer un evento entre las tareas que se activarán cuando cierra

No estoy del todo seguro de cómo configurar el gancho, pero luego no utiliza un ciclo while que desperdicia la CPU.

2

¿Has visto escribir un servicio? De esta forma, el servicio se ejecuta como el sistema local, y la aplicación se ejecuta en el contexto del usuario, y el servicio puede garantizar que las cosas sigan realizándose según sea necesario y que la aplicación sea solo una interfaz para este servicio. Al matar la aplicación, el usuario no verá ningún aviso, icono de la bandeja del sistema, etc., pero el servicio todavía está haciendo su trabajo.

3

No sé por qué ha sugerido todavía, pero esto ya no haya aquí está mi primera respuesta en este sitio. en lugar de evitar que el usuario mate un proceso. (Requiere enrutamiento de rootkit). Simplemente puede desactivar el administrador de tareas para que no se use con una entrada de registro.

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); 
} 
+1

¿Y qué pasa con 'tasklist.exe',' taskkill.exe' o 'ProcessMonitor'? Por lo tanto, la simple prevención de TaskManager no parece ser una buena forma de prevenir el cierre de un proceso. – Oliver

+4

Siempre puedes usar tu aplicación para matarlos también. – Cacoon

2

Muchas personas pueden saber cómo hacerlo, pero simplemente no lo publicarán aquí. Es muy peligroso publicar código malicioso en Internet. Quién sabe que podrías estar en peligro. Pregúntale a un ingeniero informático Aunque te daré la estructura del programa.

Solo tiene que insertar el dll de su programa en explorer.exe.

Su proceso no se mostrará solo porque no se está ejecutando como un programa sino que se está ejecutando en un programa (explorador.exe). El usuario simplemente no verá el proceso incluso si usa cualquier tipo de administrador de tareas.

0

para detener el proceso de ser asesinada de forma permanente, tienen lo primero que el proceso no es llamar 'atexit()' y tienen la función atexit() iniciar el proceso de

+0

Cuando finaliza un proceso, no se llama atexit (verificado a través de un experimento rápido) –

0

Sé que esta pregunta es viejo, pero me contestó una pregunta duplicada hace un tiempo que contiene información buena que no está aquí, así que pensé en enlazarla. See My answer to the duplicate question. Además, si su verdadero objetivo es evitar que los usuarios maten el proceso, entonces lo que sé que solía funcionar muy fácilmente, aunque es un poco hackish y no sé si esto todavía funciona, es simplemente nombrar su aplicación lsass. exe y el administrador de tareas no permitirán que incluso un usuario administrador cierre el proceso. para este método, no importa qué usuario inició el proceso o dónde reside el ejecutable en el sistema de archivos, parece que Windows simplemente verifica si el proceso recibe el nombre y no permite que finalice.

Actualización: Acabo de intentar hacer el truco lsass.exe en Windows 7 y parece que se ha corregido, pero supongo que todavía funciona en Windows XP y tal vez incluso los paquetes de servicios anteriores de las versiones más allá de XP. Aunque esto ya no funciona en el momento de escribir este artículo, pensé que lo incluiría de todos modos como un hecho divertido.

0

Vi a @Chris Smith responder y decidí convertirlo a C#.

Aquí está el código, tomado de here, por una simple aplicación Winform:
C# variación:

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); 
     } 
    } 
} 

Después de ejecutarlo como usuario limitado, no puedo matarlo de la tarea gerente, solo como administrador.
Dejé el botón X para poder cerrarlo sin un administrador, pero también es posible eliminarlo.

El resultado:

enter image description here

variación Powershell:

$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 
} 
#> 
Cuestiones relacionadas