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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read