Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide CPU Usage Not Fully Work #53

Open
wineggdrop opened this issue Nov 5, 2023 · 24 comments
Open

Hide CPU Usage Not Fully Work #53

wineggdrop opened this issue Nov 5, 2023 · 24 comments
Labels

Comments

@wineggdrop
Copy link

wineggdrop commented Nov 5, 2023

It works at the details column on task manager,but at Processes,Users and Performance columns of task manager,the cpu usage remains intact.Tested On Windows 10.
SystemProcessorPerformanceInformation and SystemProcessorIdleCycleTimeInformation never gets triggered

@bytecode77
Copy link
Owner

Yeah... I'm actually aware of this issue and documented it in section 5.4 Known Issues. Most notably is that different task managers use different routines to check CPU usage. I managed to hide CPU usage in procmon and ProcessHacker, but not in TaskMgr and PerformanceMonitor.

If I only knew, which API calls those are, I would probably also solve #7 (hiding GPU usage).

In the past, I did some blackbox testing with TaskMgr and checked various ntdll functions that were actually imported. Some of them were called, but no matter what random data I have replaced the returned data with, it didn't change the graph at all - which means I did not hook the right function.

So, starting at square one - do you or anybody who reads this have any ideas or contributions to solve this long lasting issue? I would certainly credit anybody who helps to fix bugs, even if they just know what the correct function is to hook.

@bytecode77 bytecode77 added the bug label Nov 5, 2023
@wineggdrop
Copy link
Author

wineggdrop commented Nov 5, 2023

I wish I know since the same problem has bothered me for years. I wonder if it uses pdh counter data coz taskmgr loads pdh.dll,need to test it.

@bytecode77
Copy link
Owner

Glad to hear I'm not alone...

I still have the test code that I used to find the correct function to hook. These are ones that I tried:

  • PerfQueryCounterData
  • PdhGetRawCounterArrayW
  • PdhGetFormattedCounterArrayW
  • PdhGetFormattedCounterValue
  • PdhGetRawCounterValue
  • PdhCollectQueryData
  • PdhCollectQueryDataEx
  • PdhCollectQueryDataWithTime
  • NtQueryPerformanceCounter
  • HookedNtQuerySystemInformation
    • SystemProcessorPerformanceDistribution
    • SystemBasicPerformanceInformation
    • SystemQueryPerformanceCounterInformation
    • SystemProcessorPerformanceInformationEx
    • SystemCpuQuotaInformation
    • SystemCpuSetInformation
    • SystemCpuSetTagInformation

I didn't bother to try and hook HKEY_PERFORMANCE_DATA, because I assume that TaskMgr uses WinAPI instead of this pseudo registry hive.

And to share my notes with you: TaskMgr calls NtQuerySystemInformationEx with SystemLogicalProcessorAndGroupInformation and SystemProcessorCycleTimeInformation, so I tried to set those to zero or random values, with no effect.

I have also disassembled some functions of TaskMgr and everything seems to point to PDH. That's why my testing with PDH was so extensive... I even searched for examples on how to get CPU usage using PDH to get an idea of how this library is actually used.

@wineggdrop
Copy link
Author

wineggdrop commented Nov 5, 2023

LONG GetCPUUsage()
{
HQUERY hQuery;
char CPUUsage[] = "\Processor(_Total)\% Processor Time";
HCOUNTER hCounter;
PDH_FMT_COUNTERVALUE fmtValue;
DWORD ctrType;

if (PdhOpenQuery(0,0,&hQuery) == ERROR_SUCCESS)
{
if (PdhAddCounter(hQuery,CPUUsage,0,&hCounter) == ERROR_SUCCESS)
{
if (PdhCollectQueryData(hQuery) == ERROR_SUCCESS)
{
Sleep(1000);
if (PdhCollectQueryData(hQuery) == ERROR_SUCCESS)
{
if (PdhGetFormattedCounterValue(hCounter,PDH_FMT_LONG,&ctrType,&fmtValue) == ERROR_SUCCESS)
{
PdhCloseQuery(hQuery);
return fmtValue.longValue;
}
else
{
printf("Fail To Format Value %d %d\n",GetLastError(),fmtValue.longValue);
}
}
}
else
{
printf("Fail To Collect Data\n");
}
}
else
{
printf("Fail To Open Query\n");
}
PdhCloseQuery(hQuery);
}
return -1;
}

the return value is the cpu usage.if it uses pdh,it's fine;I am afraid it uses WMI,it would be hard to hook

@bytecode77
Copy link
Owner

In issue #9 I have found out that AIDA64 is using WMI to populate process lists and that injecting WmiPrvSE.exe is required. This works automagically, because the WMI service is just using the same NtQuerySystemInformation that every application uses.

But about CPU usage... The thing is, during my tests, I only injected TaskMgr.exe and didn't bother to inject WmiPrvSE.exe with my test code. Maybe I should hook the PDH methods again and inject the WMI service and see what happens.

@wineggdrop
Copy link
Author

wish it works out

@wineggdrop
Copy link
Author

image

look like pdh getting the gpu stuff,but not cpu usage

@bytecode77
Copy link
Owner

Interesting... what tool is this?

TaskMgr (Windows 11) definitely calls PdhGetFormattedCounterArrayW and PdhCollectQueryData.

But when fuzzing this function with random values, it does not change anything in the UI - even when injecting all processes. Let me keep looking...

static PDH_STATUS WINAPI HookedPdhGetFormattedCounterArrayW(PDH_HCOUNTER hCounter, DWORD dwFormat, LPDWORD lpdwBufferSize, LPDWORD lpdwItemCount, PPDH_FMT_COUNTERVALUE_ITEM_W ItemBuffer)
{
	//TaskMgr calls this (*lpdwItemCount == 285)
	PDH_STATUS status = OriginalPdhGetFormattedCounterArrayW(hCounter, dwFormat, lpdwBufferSize, lpdwItemCount, ItemBuffer);

	if (status == ERROR_SUCCESS)
	{
		for (DWORD i = 0; i < *lpdwItemCount; i++)
		{
			ItemBuffer[i].FmtValue.longValue = GetTickCount64() % 100;
			ItemBuffer[i].FmtValue.doubleValue = (GetTickCount64() % 1000) / 1000.0;
			ItemBuffer[i].FmtValue.largeValue = GetTickCount64();
			//ItemBuffer[i].FmtValue.AnsiStringValue = "ABC";
			//ItemBuffer[i].FmtValue.WideStringValue = L"XYZ";
		}
	}

	return status;
}

@wineggdrop
Copy link
Author

the tool is dbgview,just showing the debug output
taskmgr calls pdh api,but the query is all about gpu stuff
PdhGetFormattedCounterArrayW will handle the "\GPU Engine(*)\Utilization Percentage" query value.If you hook PdhAddCounterW and save the counter handle,and comparing the counter handle from PdhGetFormattedCounterArrayW call.

@wineggdrop
Copy link
Author

wineggdrop commented Nov 7, 2023

chatgpt is very certain taskmgr using WMI to get the cpu usage,but I still doubt it.I hooked
PdhGetFormattedCounterValue
PdhGetRawCounterValue
PdhGetFormattedCounterArrayA
PdhGetFormattedCounterArrayW
PdhGetRawCounterArrayA
PdhGetRawCounterArrayW

doesn't find any query about cpu usage

image

@bytecode77
Copy link
Owner

I spent some time playing with PDH. So far, I have successfully hidden GPU usage, which was on the ToDo list for 2 years. I have commited my work in progress on this branch: https://github.com/bytecode77/r77-rootkit/tree/PDH

I hooked both PdhGetRawCounterArrayW and PdhGetFormattedCounterArrayW, checking for these weird "\GPU Engine(*)" strings. The item array contains strings like "pid_1234_luid_0x00000000_0x0000C9DE_phys_0_eng_0_engtype_3D", so I parse the PID from that string.

This is either the wrong way to go about it - or PDH is just string cancer. I don't know yet, but I'm sharing my code with you as it contains working PDH hooks. Another issue except the excessive string use is that I would really like to see some NtQuery... function to hook instead of this higher level PDH.dll.

... In the meantime, I'll do some more research about the CPU usage.

@wineggdrop
Copy link
Author

I tested GetSystemTimes(),doesn't use this API to get cpu usage as well.

@bytecode77
Copy link
Owner

GetSystemTimes calls NtQuerySystemInformation with SystemProcessorPerformanceInformation, which is what Process Hacker is using to retrieve the CPU usage. There is a hook in place.

@bytecode77
Copy link
Owner

I now know which API is used for both CPU and GPU stats. It's PcwCollectData, which is completely undocumented. Internally, it calls the driver pcw.sys by calling NtDeviceIoControlFile with \Device\PcwDrv and IOCTL 0x224013.

I'm already hooking NtDeviceIoControlFile to filter the TCPView table, which was a completely undocumented blackbox as well. Next, I'm going to figure out the PCW driver, so wish me luck...

Here's an excerpt of what the driver returns, which obviously resembles PPDH_COUNTER_INFO_W. Other binaries seem to be related to CPU usage.
image

@wineggdrop
Copy link
Author

pretty much like the gpu stuff,and good luck

@wineggdrop
Copy link
Author

GetProcessTimes API get called a lot

@wineggdrop
Copy link
Author

PcwCollectData seem to be native call of pdhcollectquerydata

@bytecode77
Copy link
Owner

So, being busy reverse engineering PDH, I have figured out how to hide GPU usage and released version 1.5.1 with that feature. I have implemented the hook in pdh.dll, not NtDeviceIoControlFile, because I couldn't reverse engineer the exact format of the data that the PCW driver returns.

CPU usage is still not fixed, but I'm at least closer, because I know more about the PWC driver now. I'll pick up on that sometime next.

@wineggdrop
Copy link
Author

good to know

@wineggdrop
Copy link
Author

wineggdrop commented Jan 12, 2024

something not about the cpu usage hiding but PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY.when r77 rootkit is installed,no doubt creating new process with PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY still get injected because r77 rookit interrupts the ntresumethread call,and inject the dll before the newer created process even starts.However,I create a process with PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON before running the install.exe,the process still get injected,I wonder why.

image

image

Maybe the reflective injection only inject codes other than pe files so it bypasses the check?

@bytecode77
Copy link
Owner

I can't find out anything useful about that "PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON", except that it's used in UpdateProcThreadAttribute prior to a call to CreateProcess. Since I never had any problems injecting processes, I didn't stumpble upon this value. Only problem I had is with services.exe, which is just impossible to inject, because it's protected.

@wineggdrop
Copy link
Author

a process with PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON set supposes to block 3rd party module injection but with Microsoft digital signature.If I inject the r77's dll with normal injection,some error windows will pop up,but it's ok using reflective injection.I assume this mechanism needs to check file for the signature,no file then no check.

image

@bytecode77
Copy link
Owner

Does this "Bad Image" error ocurr when you inject using a normal injector, or does it also happen when using the reflective loader (Test Console / full installation) ?

By the way, while I was working on the initial release in 2020, I only had a normal injector before I implemented the reflective injector. At least back then there were no issues injecting into any process. So I assume it's a new security measure.

@wineggdrop
Copy link
Author

that error won't occur using test console/full installation.As long as the injection won't involve dll file on disk,no error shows up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants
@wineggdrop @bytecode77 and others