Taskbar Modfication to Kill Windows NT/2000 Processes

The application lets you terminate any kind of running processes by
1) modifying Task Manager,
2) through a popup menu that comes up by right clicking on a tray icon.


Click here for larger image

By selecting a process from the menu, the process will be terminated. By selecting "Task Manager..." from the menu, a new Task Manager will be spawned, if one is not running yet. Then, Task Manager will be modified so that it allows the user to use "End Process" (right click on the process name in the list box) to terminate services.

There are 2 binaries involved in the operation:

killer.exe: main application
privi.dll: process privilege manipulator dll

Here is how it works:

1. First the application creates a tray icon for itself. For this, I used Chris Maunder's CSystemTray class. Thanks Chris!
(Unfortunately, couldn't find the link to his submission.)

2. On Right click on the icon, the running processes are collected and their are added to an stl vector. It is done using PSAPI.DLL:

int CKillerDlg::UpdateProcessList()
{
 typedef DWORD (WINAPI *PEnumProcesses)( DWORD*, \
                                         UINT, \
                                         DWORD* );

 typedef DWORD (WINAPI *PEnumProcessModules)( HANDLE, \
                                              HMODULE*, \
                                              UINT, \
                                              DWORD* );

 typedef DWORD (WINAPI *PGetModuleBaseName)( HANDLE, \
                                             HMODULE, \
                                             LPTSTR, \
                                             UINT );

 _TCHAR   szProcessName[MAX_PATH] = _T("unknown");
 DWORD    processID[1024], cbNeeded, cProcesses;
 UINT i;
 int result = -1;


 HINSTANCE hPsApi = LoadLibrary( _T("PSAPI.DLL") );

 if ( hPsApi == NULL )
  return result;

 PEnumProcessModules EnumProcessModules = 
 (PEnumProcessModules)GetProcAddress( hPsApi, 
                                      "EnumProcessModules" );

#ifdef UNICODE
 PGetModuleBaseName GetModuleBaseName = 
 (PGetModuleBaseName)GetProcAddress( hPsApi, 
                                     "GetModuleBaseNameW" );
#else
 PGetModuleBaseName GetModuleBaseName = 
 (PGetModuleBaseName)GetProcAddress( hPsApi, 
                                     "GetModuleBaseNameA" );
#endif

 PEnumProcesses EnumProcesses = 
 (PEnumProcesses)GetProcAddress( hPsApi, 
                                 "EnumProcesses" );

 if ( EnumProcessModules == NULL 
 || GetModuleBaseName == NULL 
 || EnumProcesses == NULL )
  return result;


 // Get the list of process identifiers.

 if ( !EnumProcesses( processID, 
                      sizeof(processID), 
                      &cbNeeded ) )
  return result;

 // Calculate how many process identifiers were returned.
 cProcesses = cbNeeded / sizeof(DWORD);

 // Print the name and process identifier for each process.

 ClearProcessList();

 for ( i = 0; i < cProcesses; i++ )
 {
  // Get a handle to the process.

  HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION 
                                 | PROCESS_VM_READ,
                                 FALSE, 
                                 processID[i] );

  if ( hProcess )
  {
   HMODULE hMod;

   if ( EnumProcessModules( hProcess, &hMod, 
                            sizeof(hMod), &cbNeeded) )
   {
    GetModuleBaseName( hProcess, hMod, szProcessName, 
                       sizeof(szProcessName) );

    ProcItemStruct *procItem = new ProcItemStruct;
    procItem->name = szProcessName;
    procItem->id = processID[i];
    this->m_processList.push_back(procItem);
   }
  }

  CloseHandle( hProcess );
 }

 return result;
}

3. Afterwards, the the vector is sorted and used to create the popup menu.

void CKillerDlg::ShowTrayPopupMenu( CWnd* pWnd, CPoint pt )
{
 CMenu menu;
 CMenu* pSubMenu = NULL;

 menu.LoadMenu( IDR_TRAYPOPUPMENU );

 pSubMenu = menu.GetSubMenu(0);

 if ( pSubMenu != NULL )
 {
  UpdateMenu( pSubMenu );

  ::SetMenuDefaultItem( pSubMenu->m_hMenu, 0, TRUE );

  UpdateProcessList();

  // Function object for sorting
  ProcItemLess pl;

  std::sort(m_processList.begin(), m_processList.end(), pl);

  for (UINT i = 0; i < m_processList.size(); i++)
  {
   // arrange items into columns
   if (i%20 == 0)
    pSubMenu->AppendMenu( MF_MENUBARBREAK | MF_STRING, 
                          KILLER_WINDOWID_FIRST + i , 
                          m_processList[i]->name );
   else
    pSubMenu->AppendMenu( MF_STRING, 
                          KILLER_WINDOWID_FIRST + i , 
                          m_processList[i]->name ); 
  }

  if ( pWnd != NULL )
   pWnd->SetForegroundWindow();

  pSubMenu->TrackPopupMenu( TPM_LEFTALIGN, 
                            pt.x, pt.y, 
                            pWnd );

  if ( pWnd != NULL )
   pWnd->SetForegroundWindow();
 }
}

4. On clicking on process names, the KillProcess function is activated:

void CKillerDlg::OnKillProcess( UINT nID )
{
 ProcItemStruct *procItem;
 procItem = m_processList[nID - KILLER_WINDOWID_FIRST];
 // Below, 3 lines are commented out, so that no 
 // annoying "Are you sure?" messagebox is shown.

 //CString str(procItem->name);
 //str.Format(_T("Name: %s \nPID: %d"), 
 //           str, 
 //           procItem->id);   
 
 //if (IDOK == ::MessageBox(NULL, 
 //                         str, 
 //                         _T("Kill?"), 
 //                         MB_OKCANCEL))

 theApp.KillProcess(procItem->id, 9);	
}

5. If the user select "Task Manager...", first there is a test for running instance. If running TaskMgr is found, the process ID is obtained, if not, a new instance is spawnd using CreateProcess(), then the process ID is stored. After this, LoadDllForRemoteThread() is called:

if (pId != 0)
 LoadDllForRemoteThread( pId, 
                         TRUE, 
                         TRUE, 
                         L"privi.dll", 
                         L"Func" );

This function was published by Zoltan Csizmadia. Thanks Zoltan!

Minor modification was made to his code. If debug code is compiled, function pointer point to a relative jump instruction (E9), and the real function code starts from where the jump is made to. Between the two addresses, variable amount of "other stuff" is present. If MAXINJECTSIZE (the size of memory that is allocated in the remote process to hold the code copied from our process) is not big enough, problems arise. By testing for the relative jump in debug mode, MAXINJECTSIZE can be reduced to the size of the function code. The following simple function is the implementation:

PVOID GetFuncAddress(PVOID addr)
{
#ifdef _DEBUG
 //check if instruction is relative jump (E9)
 if (0xE9 != *((UCHAR*)addr))
  return addr;

 // calculate base of relative jump
 ULONG base = (ULONG)((UCHAR*)addr + 5);

 // calculate offset 
 ULONG *offset = (ULONG*)((UCHAR*)addr + 1);

 return (PVOID)(base + *offset);
#else
 // in release, don't have to mess with jumps
 return addr;
#endif
}

Then it is used as follows:

WriteProcessMemory( hProcess, 
                    p, 
                    GetFuncAddress(RemoteThread), 
                    MAXINJECTSIZE, 
                    0 );

What the Func() does in privi.dll is merely enables the DEBUG process privilege:

// Set SE_DEBUG privilige for TaskMgr process
// This will make it able to kill services as well
ULONG Func()
{
 HANDLE hToken;

 if ( ! OpenProcessToken( GetCurrentProcess(), 
                          TOKEN_ADJUST_PRIVILEGES 
                          | TOKEN_QUERY, 
                          &hToken ))

  return 1;


 if ( ! SetPrivilege( hToken, SE_DEBUG_NAME, TRUE ))
 {
  CloseHandle(hToken);
  return 2;
 }

 CloseHandle(hToken);
 return 0;
}

Here is how SetPrivilege works:

// Set/Unset specified privilige for given handle
BOOL SetPrivilege(HANDLE hToken, // token handle
 LPCTSTR Privilege, // Privilege to enable/disable
 BOOL bEnablePrivilege // TRUE to enable. FALSE to disable)
{
 TOKEN_PRIVILEGES tp;
 LUID luid;
 TOKEN_PRIVILEGES tpPrevious;
 DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);

 if(!LookupPrivilegeValue( NULL, Privilege, &luid )) 
  return FALSE;

 // 
 // first pass.  get current privilege setting
 // 
 tp.PrivilegeCount           = 1;
 tp.Privileges[0].Luid       = luid;
 tp.Privileges[0].Attributes = 0;

 AdjustTokenPrivileges(hToken, 
                       FALSE,
                       tp,
                       sizeof(TOKEN_PRIVILEGES),
                       &tpPrevious,
                       &cbPrevious);

 if (GetLastError() != ERROR_SUCCESS) 
  return FALSE;

 // 
 // second pass.  set privilege based on previous setting
 // 
 tpPrevious.PrivilegeCount       = 1;
 tpPrevious.Privileges[0].Luid   = luid;

 if(bEnablePrivilege) 
 {
  tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
 }
 else 
 {
  tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
  tpPrevious.Privileges[0].Attributes);
 }

 AdjustTokenPrivileges(hToken,
                       FALSE,
                       &tpPrevious,
                       cbPrevious,
                       NULL,
                       NULL);

 if (GetLastError() != ERROR_SUCCESS) 
  return FALSE;

 return TRUE;
}

The same adjustment of privileges takes place in the killer process to allow the termination of services.

Downloads

Download executables - 12 Kb
Download source - 26 Kb


Comments

  • Taskbar Modification to Kill Windows NT/2000 Processes

    Posted by Legacy on 01/30/2004 12:00am

    Originally posted by: yogesh

    hi,
    The task mananger Concept is related to my project,
    but here u are only showing the processes which currently
    running on to the sys.

    ple. suggest me, how to get following parameter
    along with perticular process.

    - physical memory
    - virtual memory
    - page memory
    - non-paged mem.
    - CPU Usage
    - thread Count

    I am Waiting for replay..

    thank U
    yogesh



    Reply
  • Process Status

    Posted by Legacy on 01/08/2004 12:00am

    Originally posted by: Wahaj Khan

    How can I get the CPU Usage at any instant or to know about any specific process that is not running but in the Ready state. Ready state is one when CPU Usage is 00.
    Thanks

    Reply
  • Forbidden Task Manager

    Posted by Legacy on 09/30/2003 12:00am

    Originally posted by: Vrbcik

    Hi, I would like to ask - At client's site we have forbidden access to task manager (by some windows security? I don't really know - the menu item is disabled).

    I wanted to use Your programme, but it didn't run at all. Do You have an idea? Is it because of missing psapi.dll?

    Thanx for advice.

    Martin Vrbovsky

    Reply
  • http://codeguru.earthweb.com/system/killer.html

    Posted by Legacy on 09/04/2003 12:00am

    Originally posted by: stefanocri

    Excellent code, very simple to undestand

    Reply
  • How to detect whether an application has crashed

    Posted by Legacy on 10/22/2002 12:00am

    Originally posted by: R

    The application also lists an application if it has crashed.
    How can we differentiate between the working and the crashed application.
    If any one could tell me............
    I need to know if an application has crashed so that the crashed instance may be killed and a fresh instance started.

    I'll be grateful if i could be informed about this soon......
    Regards
    R

    Reply
  • How I Can End the Explorer proccess in w2k

    Posted by Legacy on 09/19/2002 12:00am

    Originally posted by: Luis Joel Almanzar

    I noticed that this program terminated the explorer process but it restarted it and I would to like to finish it completely. How can I do that? Thanks for any help.

    Luis

    Reply
  • canot adjust the priviledge!!NT, please help

    Posted by Legacy on 07/09/2002 12:00am

    Originally posted by: ese3897

    ff

    Reply
  • How to prevent a process be killed?

    Posted by Legacy on 06/20/2002 12:00am

    Originally posted by: zeuss

    Hello:
    We can use this function to kill running process,but,how to prevent a running process to be killed manually in the TaskMageger?

    Reply
  • Can't kill termsrv.exe

    Posted by Legacy on 11/01/2001 12:00am

    Originally posted by: Samen

    I login as Administrator in Windows 2000,
    and want to kill termsrv(terminal server
    service), but it was still there.
    Why it don't work?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

  • The hard facts on SaaS adoption in over 80,000 enterprises: Public vs. private companies Mid-market vs. large enterprise GoogleApps, Office365, Salesforce & more Why security is a growing concern Fill out the form to download the full cloud adoption report.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds