I recently came across a project, libprocesshider, which can be used to hide a Linux process, and out of curiosity, tried it out, and found that it involves a few interesting points, so I’ll document them.
We usually check the process by
ps command, or when the machine indicator is abnormal, we usually use
top command to check the process situation of the system, so if we make these two programs “can’t see” the process that we want to hide, won’t we complete the function of process hiding?
To make these two commands can not see our “hidden processes”, then also need to understand the principle of these two commands, their source code in the following.
- ps: https://gitlab.com/procps-ng/procps/-/tree/master/src/ps
- top: https://gitlab.com/procps-ng/procps/-/tree/master/src/top
How ps works
As you can see, ps works by traversing the
/proc directory to see a list of processes.
Then read directories
ns, etc. See:
static proc_t *simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) in
library/readproc.c file for source code.
You can see that an important system call is used here:
readdir to list the
How top works
which ? readeither : readproc means that if a process is specified, then
readproc is used, otherwise all processes are read via
readeither, so it’s essentially the same as top.
The principle of hiding
As we can see from the above, both ps and top display process information by reading the
/proc/ directory, so if we make a change here can we make it invisible to the user?
There are many ways to do this, one simple way is to replace the
readdir implementation so that when
readdir reads the
/proc/ directory it ignores the processes that we want to hide, so that when the user tries to view them via
ps they won’t be able to see them.
So how to replace it, in the past I wrote a piece of Linux read dynamic link library article, which mentioned the order of the program load dynamic link library, which has a preload part, we can through the environment variables or configuration files, in the program to load the system before the other dynamic link libraries, in advance, with our modified dynamic link library to cover the standard implementation of the standard (usually glibc), so that you can achieve the purpose of replacing the readdir, so that the purpose of the process to hide.
Suppose I have a malicious program with the file name:
evil_script.py, and I add the dynamic libraries from my mock system call to
/etc/ld.so.preload, but before replacing them, I try to see that I can see the process:
Then I added mock’s DLL to
/etc/ld.so.preload and you’ll see that ps can’t find the process.
The process just disappeared.
After we know the principle, how do we cope with it? There are many ways, for example, let me cite a few:
- Do not use dynamic link libraries, use static compilation of ps or top and other programs to view, because here just replace the dynamic link libraries, static links will not be affected;
- Find abnormal network connections, generally hidden processes will have network connections, you can find the “non-existent” process by checking the abnormal network connections;
- Manually/procedurally traverse the pid to see, since the mock system call is to determine the process name, then we skip this step, and traverse the pid list by ourselves, and read the process information inside one by one to find the “non-existent” process is also a good way;
- Check files like
/etc/ld.so.preload, since we know there might be a problem here, we can just check it.
This article started with a small project to understand a way to “hide” a process, and briefly introduced its principle (however, I did not go deeper into the source code of
ps than I had tasted), and provided a real-life example to demonstrate it. Finally, it also gives a personal response based on my own understanding, which may not be the best method, but I think it should solve some problems.