Skip to content

Instantly share code, notes, and snippets.

@dreness
Last active November 20, 2023 11:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dreness/e4d699a73037db9733f69c2629b82a4f to your computer and use it in GitHub Desktop.
Save dreness/e4d699a73037db9733f69c2629b82a4f to your computer and use it in GitHub Desktop.
pidpersec.c
/*
By Andre LaBranche, with extensive help from your friend and mine, ChatGPT.
In other words, assume this code eats babies.
This program attempts to be a reasonably inexpensive replacement for
/usr/bin/pidpersec.d in macOS, which still ships, but is broken by default
(i.e. when SIP is enabled).
compile with: clang -o pidpersec pidpersec.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
// warn if the current major OS version isn't known to work with this script
void warn_if_untested_OS(void)
{
char *os_version = malloc(128);
size_t len = 128;
if (sysctlbyname("kern.osproductversion", os_version, &len, NULL, 0) == -1)
{
perror("sysctlbyname");
exit(1);
}
if (os_version == NULL)
{
fprintf(stderr, "Unable to determine OS version.\n");
exit(1);
}
if (os_version[0] == '1' && os_version[1] != '4')
{
fprintf(stderr, "Detected macOS version %s.\n", os_version);
fprintf(stderr, "This tool was created and used on macOS 14 - YMMV.\n");
}
}
// function to preflight a given mib to make sure it works
int preflight_sysctl(char *sysctl_name, size_t len)
{
// Query the value type of the specified sysctl
if (sysctlbyname(sysctl_name, NULL, &len, NULL, 0) == -1)
{
perror("sysctlbyname");
return 1;
}
return 0;
};
// function to fetch and return the value of a given sysctl mib
long fetch_sysctl(char *sysctl_name) {
long value;
size_t len = sizeof(long);
// Query the sysctl value by name
if (sysctlbyname(sysctl_name, &value, &len, NULL, 0) == -1)
{
perror("sysctlbyname");
exit(1);
}
// Check if the sysctl call was successful
if (len != sizeof(long))
{
fprintf(stderr, "Unexpected data size for sysctl %s\nSorry ol' chap.\n", sysctl_name);
exit(1);
}
return value;
}
int main(int argc, char *argv[])
{
warn_if_untested_OS();
// I doubt the presence of these mibs is gauranteed.
char *exec_hook_count = "security.mac.asp.stats.exec_hook_count";
//char *exec_hook_work_time = "security.mac.asp.stats.exec_hook_work_time";
// Accept a polling interval as an argument, default to 1 second
double poll_interval = 1.0;
if (argc > 1)
{
poll_interval = atof(argv[1]);
}
// printf("Using interval: %.2f seconds\n", poll_interval);
// We'll pass this to usleep, which takes microseconds, so multiply by 1 million
int poll_interval_ms = poll_interval * 1000000;
// Values of the sysctl mibs we're polling
long exec_hook_count_value;
// long exec_hook_work_time_value;
// The previous value of the sysctl mib we're polling.
// We'll use -1 to indicate that we haven't polled yet.
long exec_hook_count_prev = -1;
//long exec_hook_work_time_prev = -1;
// The difference between the current and previous values we're polling.
long exec_hook_count_delta;
// long exec_hook_work_time_delta;
size_t len = sizeof(long);
preflight_sysctl(exec_hook_count, len);
// preflight_sysctl(exec_hook_work_time, len);
{
/*
len is supposed to be an integer value that's more like an enum that maps to a sysctl value type.
I'm not sure how to programmatically derive this mapping, or even get the type name for a given value.
However, I did collect all of the different shorthand values for these types according to /usr/sbin/sysctl:
sudo sysctl -d -a | cut -d: -f2 | egrep '\S' | sort | uniq -c | egrep -v ',' | sort -n
There are actually a whole bunch, but we're just interested in the shortest descriptors used by a majority of the sysctl values.
I asked ChatGPT what these mean:
sysctl -a -d in macOS shows that values have the following types. What do these mean?
I
IU
L
LU
N
NVMe
P
Q
S
The reply was good and described the types referred to by these characters (integer, unsigned integer, long,
unsigned long, node, pointer, quad, string) but didn't describe how I could do that lookup myself, so let's ask ChatGPT:
how do I interpret the value type returned by this code:
// Query the value type of the specified sysctl
if (sysctlbyname(sysctl_name, &type, &type_size, NULL, 0) == -1) {
perror("sysctlbyname");
return 1;
}
ChatGPT responded:
The value type returned by the code you provided represents the data type of the sysctl variable
identified by sysctl_name. This data type provides information about the format of the value
returned by the sysctl query.
When you query a sysctl using the sysctlbyname function, the value type (type) is returned as
an integer, which corresponds to one of the data types mentioned earlier (e.g., I, IU, L, LU, N, NVMe, P, Q, S).
To interpret the value type, you can use the following mappings:
1: Integer (I)
2: Unsigned Integer (IU)
3: Long (L)
4: Unsigned Long (LU)
5: Node (N)
6: NVMe (specialized type related to NVMe SSDs)
7: Pointer (P)
8: Quad (Q)
9: String (S)
In the code you provided, the type variable will contain an integer corresponding to the data
type of the sysctl value specified by sysctl_name. You can compare this integer to the mappings
above to determine the data type of the value. For example, if type is 1, it indicates that the
sysctl value is of type "I" (integer).
Based on the value type, you can interpret how to handle and format the value when you retrieve it using
sysctl. Different value types require different methods of interpretation, casting, and printing, and
this interpretation depends on the specific sysctl you are querying.
Why you being coy, ChatGPT?! Two more prompts later and it claimed to have a solution... but it didn't work. Looking up
the function used suggested the code is appropriate for some other *nix. So I say:
now give me a version that works on macOS please
As far as I can tell, the following answer is correct - which makes it all the more curious that ChatGPT knows the mapping!
On macOS, there isn't a direct built-in sysctl request for obtaining the type name of a sysctl variable.
The sysctl command doesn't provide type name information in its output either. The information about
sysctl types is generally documented in the manual pages (man pages) and available in the header files
for system libraries, but it's not directly accessible at runtime.
*/
}
// poll sysctl and print deltas since the previous interval.
while (1)
{
// fetch the current values of the sysctl mib
exec_hook_count_value = fetch_sysctl(exec_hook_count);
// exec_hook_work_time_value = fetch_sysctl(exec_hook_work_time);
// we don't have a delta on the first lap
if (exec_hook_count_prev < 0)
{
exec_hook_count_delta = 0;
// exec_hook_work_time_delta = 0;
}
else
{
exec_hook_count_delta = exec_hook_count_value - exec_hook_count_prev;
// exec_hook_work_time_delta = exec_hook_work_time_value - exec_hook_work_time_prev;
// printf("%ld,%ld\n", exec_hook_count_delta, exec_hook_work_time_delta);
printf("%ld\n", exec_hook_count_delta);
fflush(stdout);
}
// Update the previous value for the next iteration, sleep
exec_hook_count_prev = exec_hook_count_value;
// exec_hook_work_time_prev = exec_hook_work_time_value;
usleep(poll_interval_ms);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment