mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-06 17:45:22 +00:00
Add per process performance data to windows processes table (#5224)
This commit is contained in:
parent
b8d7243aa9
commit
e6302379fd
@ -49,6 +49,9 @@ const std::map<unsigned long, std::string> kMemoryConstants = {
|
||||
{PAGE_NOCACHE, "PAGE_NOCACHE"},
|
||||
{PAGE_WRITECOMBINE, "PAGE_WRITECOMBINE"},
|
||||
};
|
||||
const std::string kWinProcPerfQuery =
|
||||
"SELECT IDProcess, ElapsedTime, HandleCount, PercentProcessorTime FROM "
|
||||
"Win32_PerfRawData_PerfProc_Process";
|
||||
|
||||
/// Given a pid, enumerates all loaded modules and memory pages for that process
|
||||
Status genMemoryMap(unsigned long pid, QueryData& results) {
|
||||
@ -144,32 +147,40 @@ Status getProcList(std::set<long>& pids) {
|
||||
return Status(0, "Ok");
|
||||
}
|
||||
|
||||
void genProcess(const WmiResultItem& result, QueryData& results_data) {
|
||||
Row r;
|
||||
void genProcess(const long pid,
|
||||
const WmiResultItem& result,
|
||||
Row& r,
|
||||
QueryContext& context) {
|
||||
Status s;
|
||||
long pid;
|
||||
long lPlaceHolder;
|
||||
std::string sPlaceHolder;
|
||||
|
||||
/// Store current process pid for more efficient API use.
|
||||
auto currentPid = GetCurrentProcessId();
|
||||
|
||||
s = result.GetLong("ProcessId", pid);
|
||||
r["pid"] = s.ok() ? BIGINT(pid) : BIGINT(-1);
|
||||
|
||||
long uid = -1;
|
||||
long gid = -1;
|
||||
HANDLE hProcess = nullptr;
|
||||
if (pid == currentPid) {
|
||||
hProcess = GetCurrentProcess();
|
||||
} else {
|
||||
hProcess =
|
||||
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
||||
uid = 0;
|
||||
gid = 0;
|
||||
if (context.isAnyColumnUsed({"uid",
|
||||
"gid",
|
||||
"cwd",
|
||||
"root",
|
||||
"user_time",
|
||||
"system_time",
|
||||
"start_time",
|
||||
"is_elevated_token"})) {
|
||||
if (pid == currentPid) {
|
||||
hProcess = GetCurrentProcess();
|
||||
} else {
|
||||
hProcess =
|
||||
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
||||
uid = 0;
|
||||
gid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
result.GetString("Name", r["name"]);
|
||||
@ -190,15 +201,17 @@ void genProcess(const WmiResultItem& result, QueryData& results_data) {
|
||||
result.GetString("VirtualSize", sPlaceHolder);
|
||||
r["total_size"] = BIGINT(sPlaceHolder);
|
||||
|
||||
std::vector<char> fileName(MAX_PATH + 1, 0x0);
|
||||
if (pid == currentPid) {
|
||||
GetModuleFileName(nullptr, fileName.data(), MAX_PATH);
|
||||
} else {
|
||||
GetModuleFileNameEx(hProcess, nullptr, fileName.data(), MAX_PATH);
|
||||
}
|
||||
if (context.isAnyColumnUsed({"cwd", "root"})) {
|
||||
std::vector<char> fileName(MAX_PATH + 1, 0x0);
|
||||
if (pid == currentPid) {
|
||||
GetModuleFileName(nullptr, fileName.data(), MAX_PATH);
|
||||
} else {
|
||||
GetModuleFileNameEx(hProcess, nullptr, fileName.data(), MAX_PATH);
|
||||
}
|
||||
|
||||
r["cwd"] = SQL_TEXT(fileName.data());
|
||||
r["root"] = r["cwd"];
|
||||
r["cwd"] = SQL_TEXT(fileName.data());
|
||||
r["root"] = r["cwd"];
|
||||
}
|
||||
|
||||
r["pgroup"] = "-1";
|
||||
r["euid"] = "-1";
|
||||
@ -206,69 +219,102 @@ void genProcess(const WmiResultItem& result, QueryData& results_data) {
|
||||
r["egid"] = "-1";
|
||||
r["sgid"] = "-1";
|
||||
|
||||
FILETIME createTime;
|
||||
FILETIME exitTime;
|
||||
FILETIME kernelTime;
|
||||
FILETIME userTime;
|
||||
auto procRet =
|
||||
GetProcessTimes(hProcess, &createTime, &exitTime, &kernelTime, &userTime);
|
||||
if (procRet == FALSE) {
|
||||
r["user_time"] = BIGINT(-1);
|
||||
r["system_time"] = BIGINT(-1);
|
||||
r["start_time"] = BIGINT(-1);
|
||||
} else {
|
||||
// Windows stores proc times in 100 nanosecond ticks
|
||||
ULARGE_INTEGER utime;
|
||||
utime.HighPart = userTime.dwHighDateTime;
|
||||
utime.LowPart = userTime.dwLowDateTime;
|
||||
r["user_time"] = BIGINT(utime.QuadPart / 10000);
|
||||
utime.HighPart = kernelTime.dwHighDateTime;
|
||||
utime.LowPart = kernelTime.dwLowDateTime;
|
||||
r["system_time"] = BIGINT(utime.QuadPart / 10000);
|
||||
r["start_time"] = BIGINT(osquery::filetimeToUnixtime(createTime));
|
||||
if (context.isAnyColumnUsed({"user_time", "system_time", "start_time"})) {
|
||||
FILETIME createTime;
|
||||
FILETIME exitTime;
|
||||
FILETIME kernelTime;
|
||||
FILETIME userTime;
|
||||
auto procRet = GetProcessTimes(
|
||||
hProcess, &createTime, &exitTime, &kernelTime, &userTime);
|
||||
if (procRet == FALSE) {
|
||||
r["user_time"] = BIGINT(-1);
|
||||
r["system_time"] = BIGINT(-1);
|
||||
r["start_time"] = BIGINT(-1);
|
||||
} else {
|
||||
// Windows stores proc times in 100 nanosecond ticks
|
||||
ULARGE_INTEGER utime;
|
||||
utime.HighPart = userTime.dwHighDateTime;
|
||||
utime.LowPart = userTime.dwLowDateTime;
|
||||
r["user_time"] = BIGINT(utime.QuadPart / 10000);
|
||||
utime.HighPart = kernelTime.dwHighDateTime;
|
||||
utime.LowPart = kernelTime.dwLowDateTime;
|
||||
r["system_time"] = BIGINT(utime.QuadPart / 10000);
|
||||
r["start_time"] = BIGINT(osquery::filetimeToUnixtime(createTime));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the process UID and GID from its SID
|
||||
HANDLE tok = nullptr;
|
||||
std::vector<char> tokUser(sizeof(TOKEN_USER), 0x0);
|
||||
auto ret = OpenProcessToken(hProcess, TOKEN_READ, &tok);
|
||||
if (ret != 0 && tok != nullptr) {
|
||||
unsigned long tokOwnerBuffLen;
|
||||
ret = GetTokenInformation(tok, TokenUser, nullptr, 0, &tokOwnerBuffLen);
|
||||
if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
tokUser.resize(tokOwnerBuffLen);
|
||||
ret = GetTokenInformation(
|
||||
tok, TokenUser, tokUser.data(), tokOwnerBuffLen, &tokOwnerBuffLen);
|
||||
}
|
||||
if (context.isAnyColumnUsed({"uid", "gid", "is_elevated_token"})) {
|
||||
/// Get the process UID and GID from its SID
|
||||
HANDLE tok = nullptr;
|
||||
std::vector<char> tokUser(sizeof(TOKEN_USER), 0x0);
|
||||
auto ret = OpenProcessToken(hProcess, TOKEN_READ, &tok);
|
||||
if (ret != 0 && tok != nullptr) {
|
||||
unsigned long tokOwnerBuffLen;
|
||||
ret = GetTokenInformation(tok, TokenUser, nullptr, 0, &tokOwnerBuffLen);
|
||||
if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
tokUser.resize(tokOwnerBuffLen);
|
||||
ret = GetTokenInformation(
|
||||
tok, TokenUser, tokUser.data(), tokOwnerBuffLen, &tokOwnerBuffLen);
|
||||
}
|
||||
|
||||
// Check if the process is using an elevated token
|
||||
auto elevated = FALSE;
|
||||
TOKEN_ELEVATION Elevation;
|
||||
DWORD cbSize = sizeof(TOKEN_ELEVATION);
|
||||
if (GetTokenInformation(
|
||||
tok, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
|
||||
elevated = Elevation.TokenIsElevated;
|
||||
}
|
||||
// Check if the process is using an elevated token
|
||||
auto elevated = FALSE;
|
||||
TOKEN_ELEVATION Elevation;
|
||||
DWORD cbSize = sizeof(TOKEN_ELEVATION);
|
||||
if (GetTokenInformation(
|
||||
tok, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
|
||||
elevated = Elevation.TokenIsElevated;
|
||||
}
|
||||
|
||||
r["is_elevated_token"] = elevated ? INTEGER(1) : INTEGER(0);
|
||||
}
|
||||
if (uid != 0 && ret != 0 && !tokUser.empty()) {
|
||||
auto sid = PTOKEN_OWNER(tokUser.data())->Owner;
|
||||
r["uid"] = INTEGER(getUidFromSid(sid));
|
||||
r["gid"] = INTEGER(getGidFromSid(sid));
|
||||
} else {
|
||||
r["uid"] = INTEGER(uid);
|
||||
r["gid"] = INTEGER(gid);
|
||||
r["is_elevated_token"] = elevated ? INTEGER(1) : INTEGER(0);
|
||||
}
|
||||
if (uid != 0 && ret != 0 && !tokUser.empty()) {
|
||||
auto sid = PTOKEN_OWNER(tokUser.data())->Owner;
|
||||
r["uid"] = INTEGER(getUidFromSid(sid));
|
||||
r["gid"] = INTEGER(getGidFromSid(sid));
|
||||
} else {
|
||||
r["uid"] = INTEGER(uid);
|
||||
r["gid"] = INTEGER(gid);
|
||||
}
|
||||
if (tok != nullptr) {
|
||||
CloseHandle(tok);
|
||||
tok = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (hProcess != nullptr) {
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
if (tok != nullptr) {
|
||||
CloseHandle(tok);
|
||||
tok = nullptr;
|
||||
}
|
||||
|
||||
// collect perf data into a hashmap by pid to later be refferenced
|
||||
|
||||
void genPerfPerProcess(
|
||||
std::map<std::int32_t, std::map<std::string, std::int64_t>>& perfData) {
|
||||
const WmiRequest request(kWinProcPerfQuery);
|
||||
|
||||
if (!request.getStatus().ok()) {
|
||||
VLOG(1) << "Failed to query process perf data from WMI";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& results = request.results();
|
||||
for (const auto& result : results) {
|
||||
std::map<std::string, std::int64_t> processData;
|
||||
long processID;
|
||||
long handleCount = 0;
|
||||
std::string elapsedTime;
|
||||
std::string percentProcessorTime;
|
||||
|
||||
result.GetString("ElapsedTime", elapsedTime);
|
||||
result.GetLong("HandleCount", handleCount);
|
||||
result.GetString("PercentProcessorTime", percentProcessorTime);
|
||||
processData["elapsed_time"] = std::stoll(elapsedTime);
|
||||
processData["handle_count"] = handleCount;
|
||||
processData["percent_processor_time"] = std::stoll(percentProcessorTime);
|
||||
result.GetLong("IDProcess", processID);
|
||||
perfData[processID] = processData;
|
||||
}
|
||||
results_data.push_back(r);
|
||||
}
|
||||
|
||||
QueryData genProcesses(QueryContext& context) {
|
||||
@ -300,13 +346,36 @@ QueryData genProcesses(QueryContext& context) {
|
||||
}
|
||||
}
|
||||
|
||||
// get per process data
|
||||
std::map<std::int32_t, std::map<std::string, std::int64_t>> perfData;
|
||||
if (context.isAnyColumnUsed(
|
||||
{"elapsed_time", "handle_count", "percent_processor_time"})) {
|
||||
genPerfPerProcess(perfData);
|
||||
}
|
||||
|
||||
const WmiRequest request(query);
|
||||
if (request.getStatus().ok()) {
|
||||
for (const auto& item : request.results()) {
|
||||
long pid = 0;
|
||||
Row r;
|
||||
if (item.GetLong("ProcessId", pid).ok()) {
|
||||
genProcess(item, results);
|
||||
r["pid"] = BIGINT(pid);
|
||||
// add per process perf data
|
||||
if (context.isAnyColumnUsed(
|
||||
{"elapsed_time", "handle_count", "percent_processor_time"})) {
|
||||
std::map<std::string, std::int64_t> procPerfData;
|
||||
procPerfData = perfData[pid];
|
||||
r["elapsed_time"] = BIGINT(procPerfData["elapsed_time"]);
|
||||
r["handle_count"] = BIGINT(procPerfData["handle_count"]);
|
||||
r["percent_processor_time"] =
|
||||
BIGINT(procPerfData["percent_processor_time"]);
|
||||
}
|
||||
|
||||
genProcess(pid, item, r, context);
|
||||
} else {
|
||||
r["pid"] = BIGINT(-1);
|
||||
}
|
||||
results.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,17 @@ QueryData genTime(QueryContext& context) {
|
||||
|
||||
char iso_8601[21] = {0};
|
||||
strftime(iso_8601, sizeof(iso_8601), "%FT%TZ", &gmt);
|
||||
|
||||
#ifdef WIN32
|
||||
if (context.isColumnUsed("win_timestamp")) {
|
||||
FILETIME ft = {0};
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
LARGE_INTEGER li = {0};
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
long long int hns = li.QuadPart;
|
||||
r["win_timestamp"] = BIGINT(hns);
|
||||
}
|
||||
#endif
|
||||
r["weekday"] = SQL_TEXT(weekday);
|
||||
r["year"] = INTEGER(now.tm_year + 1900);
|
||||
r["month"] = INTEGER(now.tm_mon + 1);
|
||||
@ -84,5 +94,5 @@ QueryData genTime(QueryContext& context) {
|
||||
results.push_back(r);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace tables
|
||||
} // namespace osquery
|
||||
|
@ -33,8 +33,10 @@ schema([
|
||||
])
|
||||
extended_schema(WINDOWS, [
|
||||
Column("is_elevated_token", INTEGER, "Process uses elevated token yes=1, no=0"),
|
||||
Column("elapsed_time", BIGINT, "Elapsed time in seconds this process has been running."),
|
||||
Column("handle_count", BIGINT, "Total number of handles that the process has open. This number is the sum of the handles currently opened by each thread in the process."),
|
||||
Column("percent_processor_time", BIGINT, "Returns elapsed time that all of the threads of this process used the processor to execute instructions in 100 nanoseconds ticks."),
|
||||
])
|
||||
|
||||
extended_schema(DARWIN, [
|
||||
Column("upid", BIGINT, "A 64bit pid that is never reused. Returns -1 if we couldn't gather them from the system."),
|
||||
Column("uppid", BIGINT, "The 64bit parent pid that is never reused. Returns -1 if we couldn't gather them from the system."),
|
||||
|
@ -19,5 +19,8 @@ schema([
|
||||
aliases=["date_time"]),
|
||||
Column("iso_8601", TEXT, "Current time (ISO format) in the system"),
|
||||
])
|
||||
extended_schema(WINDOWS, [
|
||||
Column("win_timestamp", BIGINT, "Timestamp value in 100 nanosecond units."),
|
||||
])
|
||||
attributes(utility=True)
|
||||
implementation("time@genTime")
|
||||
|
Loading…
Reference in New Issue
Block a user