I'm on an x64 machine and I really cbf setting up a kernel debugger on my XP x86 VM, so for now a static analysis will have to suffice.
They seem to perform a single kernelmode hook, it is on ZwQuerySystemInformation. They use it to hide processes and adjust some processor information.
The former they use because their bot uses a process for its GUI. What they don't seem to have realized is:
- Warden does NOT scan processes.
- They got the process names wrong in their hook, so even if Warden was looking for them, their hook wouldn't work. (Lol)
- There are information codes other than SystemProcessInformation which return the exact same list, but with just a tiny bit of extra information. All of these codes must be handled otherwise their hook can by bypassed. (Good luck finding all of them, most are 100% undocumented and will require reversing of the Windows kernel.)
The latter I have no idea about. I was hoping someone with some more kernelmode experience could shed some light on that.
Overall their driver seems to be very poorly written, and is obviously the work of a complete amateur. I mean, shit, I have very little kernel-mode experience and even I could do a better job than this clusterfuck.
Overall their driver seems to be very poorly written, and is obviously the work of a complete amateur. I mean, shit, I have very little kernel-mode experience and even I could do a better job than this clusterfuck.
Here is the full dump of their hook sub, cleaned up, named, and documented by Kynox and I.
NTSTATUS __stdcall ZwQuerySystemInformation_Hook(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength)
{
PVOID pCurrentEntry; // ebx@1
PVOID pPreviousEntry; // ebp@3
unsigned int TempIdleTime1; // eax@7
unsigned __int8 TempIdleTimeFlag1; // cf@7
unsigned int TempKernelTime1; // eax@7
unsigned __int8 TempKernelTimeFlag1; // cf@7
unsigned __int8 TempIdleTimeFlag2; // cf@11
unsigned __int8 TempKernelTimeFlag2; // cf@11
int IdleTimeTemp2; // ecx@18
unsigned __int8 IdleTimeTemp3; // cf@18
NTSTATUS TrampResult; // [sp+1Ch] [bp+10h]@1
pCurrentEntry = SystemInformation;
// Call original API
TrampResult = ZwQuerySystemInformationTramp(
SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength);
// Check if original API succeeded
if ( TrampResult >= 0 )
{
// Check for SystemProcessInformation
if ( SystemInformationClass == 5 )
{
pPreviousEntry = 0;
if ( SystemInformation )
{
do
{
// Check pCurrent->ImageName.Buffer
if ( *((_DWORD *)pCurrentEntry + 15) )
{
// Check for the presence of a couple of unknown processes.
// This conditional will always fail because there are no processes used by these names!
if ( !strncmp(*((const char **)pCurrentEntry + 15), (const char *)L"wow007", 12)
|| !strncmp(*((const char **)pCurrentEntry + 15), (const char *)L"wowmf", 10) )
{
// Do something with pCurrentEntry->UniqueProcessId
sub_11151(*((_DWORD *)pCurrentEntry + 17));
// Modify pCurrentEntry->IdleTime
TempIdleTime1 = *((_DWORD *)pCurrentEntry + 10);
TempIdleTimeFlag1 = TempIdleTime1 >= -(_DWORD)qword_120F8;
LODWORD(qword_120F8) = TempIdleTime1 + qword_120F8;
HIDWORD(qword_120F8) += *((_DWORD *)pCurrentEntry + 11) + TempIdleTimeFlag1;
// Modify pCurrentEntry->KernelTime
TempKernelTime1 = *((_DWORD *)pCurrentEntry + 12);
TempKernelTimeFlag1 = TempKernelTime1 >= -(_DWORD)qword_120E8;
LODWORD(qword_120E8) = TempKernelTime1 + qword_120E8;
HIDWORD(qword_120E8) += *((_DWORD *)pCurrentEntry + 13) + TempKernelTimeFlag1;
// Advance to next entry if there are still entries to process
if ( pPreviousEntry )
{
if ( *(_DWORD *)pCurrentEntry )
*(_DWORD *)pPreviousEntry += *(_DWORD *)pCurrentEntry;
else
*(_DWORD *)pPreviousEntry = 0;
}
}
}
else
{
// Modify pCurrentEntry->IdleTime and pCurrentEntry->KernelTime even on unnamed processes
TempIdleTimeFlag2 = (_DWORD)qword_120F8 >= (unsigned int)-*((_DWORD *)pCurrentEntry + 10);
*((_DWORD *)pCurrentEntry + 10) += qword_120F8;
*((_DWORD *)pCurrentEntry + 11) += HIDWORD(qword_120F8) + TempIdleTimeFlag2;
TempKernelTimeFlag2 = (_DWORD)qword_120E8 >= (unsigned int)-*((_DWORD *)pCurrentEntry + 12);
*((_DWORD *)pCurrentEntry + 12) += qword_120E8;
*((_DWORD *)pCurrentEntry + 13) += HIDWORD(qword_120E8) + TempKernelTimeFlag2;
qword_120E8 = 0i64;
qword_120F8 = 0i64;
}
// Advance to next entry if there are still entries to process
pPreviousEntry = pCurrentEntry;
if ( *(_DWORD *)pCurrentEntry )
pCurrentEntry = (char *)pCurrentEntry + *(_DWORD *)pCurrentEntry;
else
pCurrentEntry = 0;
}
while ( pCurrentEntry );
}
}
else
{
// SystemProcessorPerformanceInformation
if ( SystemInformationClass == 8 )
{
// Increase IdleTime
IdleTimeTemp2 = (unsigned __int64)(qword_120F8 + qword_120E8) >> 32;
IdleTimeTemp3 = (_DWORD)qword_120F8 + (_DWORD)qword_120E8 >= (unsigned int)-*(_DWORD *)SystemInformation;
*(_DWORD *)SystemInformation += qword_120F8 + qword_120E8;
*((_DWORD *)SystemInformation + 1) += IdleTimeTemp2 + IdleTimeTemp3;
}
}
}
return TrampResult;
}
