bytecode77 / r77-rootkit

Fileless ring 3 rootkit with installer and persistence that hides processes, files, network connections, etc.
https://bytecode77.com/r77-rootkit
BSD 2-Clause "Simplified" License
1.55k stars 383 forks source link

Hide CPU Usage Not Fully Work #53

Open wineggdrop opened 8 months ago

wineggdrop commented 8 months ago

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 commented 8 months ago

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.

wineggdrop commented 8 months ago

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 commented 8 months ago

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:

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

wish it works out

wineggdrop commented 8 months ago

image

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

bytecode77 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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

bytecode77 commented 8 months ago

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

bytecode77 commented 8 months ago

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 commented 8 months ago

pretty much like the gpu stuff,and good luck

wineggdrop commented 8 months ago

GetProcessTimes API get called a lot

wineggdrop commented 8 months ago

PcwCollectData seem to be native call of pdhcollectquerydata

bytecode77 commented 7 months ago

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 commented 7 months ago

good to know

wineggdrop commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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.