Skip to main content
A Mac OS X Rootkit Uses the Tricks You Haven’t Known Yet 2 - Detecting a Process Hidden by Rubilyn

A Mac OS X Rootkit Uses the Tricks You Haven’t Known Yet 2 - Detecting a Process Hidden by Rubilyn

Read previous part: You Can’t See Me: A Mac OS X Rootkit Uses the Tricks You Haven’t Known Yet

Expert from Taiwan by the handle TT continues the presentation by elaborating on the cat and mouse game of hiding and detecting a random process on Mac OS X.

Detecting Rubilyn process hiding is trivial. The way Rubilyn hides a process is very simple – it just unlinks p_list to hide a process. So detecting Rubilyn is not difficult. We can easily detect Rubilyn process hiding by listing tasks and comparing them with processes. If a process is not showing up in the tasks, it means someone did process unlinking.

Rubilyn actually cannot hide from Active Monitor. It can only hide from the ‘ps’ command, but if you use Active Monitor it will see the process hidden by Rubilyn. Another thing is Rubilyn can only run on Mac OS X 10.7.

Volatility is a well-known memory forensic tool. New version of Volatility supports Mac OS X, so it can scan memory dump from Mac OS. It can detect the Rubilyn rootkit as well.

In Mac OS kernel, processes and tasks are actually very complicated. There are several linked lists to chain those objects, not only processes and tasks – there are a lot. Volatility checks several linked lists as well to make sure there is no invisible process. So, when we try to bypass Volatility we unlink p_list, we unlink p_hash, we unlink p_pglist, and we unlink task. Then we can bypass Volatility.

Okay, let’s see a demo (video above). In the beginning we launch Calculator, and PID of the Calculator is 200. Then we load our rootkit, our kernel module. We specify the PID that we would like to hide. As you can see, Calculator is now missing in the Activity Monitor. So this process is invisible. Now we are trying to dump memory, it takes some time. Okay, now we can use Volatility to parse the memory dump and see if there is the process for Calculator. As you can see, it is sorted by PID, and the PID 200 is missing, it’s not showing up here. So, with this approach we can bypass Volatility.

Hiding and detecting a process is like a cat and mouse game in kernel (see right-hand image). When someone releases a new way to detect, there is always a new way to bypass. If you have some approach, someone will definitely bypass you or detect you in the future. Doing this stuff is a really hard work. You need to dig into complicated kernel objects, you need to implement kernel module, you need to guarantee the stability of your rootkit.

However, during this research we noticed something very interesting. Let’s see a demo (watch video above). Now we use the rootkit we have to hide a process, and we found something very interesting. Okay, we launch Calculator, and the PID is 425. Now we use our rootkit to hide this process. You cannot see Calculator now. And one day we noticed that there is a command with which you can actually very easily see all processes no matter what rootkit you used to hide a process. You see that ‘launchctl list ǀ grep’ found your PID, so you can see this process. So, what happened? Let me explain.

We call it the ‘user mode magic’. On the demo you can see the command we used to list all processes. This command is purely a user mode application. All the data it uses is in user mode. And it is a very simple way to detect an invisible process from user space, no matter what magic you are using in kernel, and no matter how many kernel objects you modify. So the user mode tool can list all processes that you hide in kernel.

It is because of launchd (see image above), which is monitoring all processes creation and termination. It is in charge of process creation. So it maintains the jobs list in user mode. And launchctl is the tool to communicate with launchd; it can easily list all jobs. So, if you want to hide a process you have to deal with launchd.

Now we are going to see more details of launchd (see image above). Since launchctl can list processes, we start from here. You can see this function (see image below).

This is the key part. So we call this command, and it sends a message to launchd, and finally it will call this function (see image below) to send a message to launchd.

In launchd, the function is in charge of the list; all jobs are in this function. As you can see here, the job object is actually the process in launchd.

So we can look for the job in launchd, then we can list all processes or we can try to unlink lists (see image above).

Each job has ‘jobmgr’ structure, so we need to look for jobmgr in launchd (see image below).

However, the symbol in launchd is removed, so here are some hints for you about how to reverse-engineer the launchd (see image below).

We use some string to identify the code. Here is the source code, but in launchd you cannot see function symbol. So we use this string to identify the code and try to look for the jobmgr structure (see image below).

In this function you can also see there’s ‘root_jobmgr’ (see image below).

So, from this function, if we can access this object (root_jobmgr), we can list all jobs or look for a specific job in launchd. But it is not easy because there is no symbol.

So we analyzed the root_jobmgr and we found it is in the data section (see image above).

So the way we unlink a job in launchd is: 1) we have to get root permission; 2) enumerate process launchd and get launchd task; 3) try to read launchd memory and find data section; 4) the most important thing is to find root_jobmgr, and you have to try the elements in data section of the launchd; 5) if we get the root_jobmgr, then we can start to enumerate jobmgr and get job; 6) then, using the linked list in between the jobs, we can find the target job; 7) and we unlink the job (see image above).

We have a short demo here as well (watch video above). So, we use a rootkit to hide the process. But we are in user mode and we can still use launchctl to see the process. Now we will try to modify the data in launchd – then we can hide the process. The ‘list’ tool can unlink the job in launchd. We will try to find the job again, but it’s invisible now. So, to hide a process in Mac OS you need to deal with something in kernel and user space as well.

Read next part: A Mac OS X Rootkit Uses the Tricks You Haven’t Known Yet 3 - Benefits of the Host Privilege