- Saved searches
- Use saved searches to filter your results more quickly
- License
- edouardpoitras/process_injection_example
- Name already in use
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
- About
- How do I run command hooks when a specific process is started?
- 1 Answer 1
- Hook action on process creation
- 2 Answers 2
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
Linux Remote Process Injection and Hooking Example — htop
License
edouardpoitras/process_injection_example
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Linux Remote Process Injection and Hooking — htop
I was very curious how someone would go about doing this. There seems to be plenty of tools (Detours, EasyHook, etc) and documentation online on how to hook into running processes in Windows, but I had a hard time finding a concrete example online on how to do it in Linux. This is the repository I wish I had found 3 days ago.
For the sake of this exercise, we are going to assume that htop is not open-source.
We want be able to change the uptime value displayed by htop while it’s running.
For this we need the name and signature of the function responsible.
Fire up htop in a terminal window and get it’s PID while you’re in there.
You can also get the PID with something like this: ps aux | grep htop | head -n 1
In another terminal window, start a debug session with the htop process using it’s PID:
Once attached to the running process, we want to find the function. Let’s start by searching for a function that has the word uptime in it:
(gdb) set case-sensitive off (gdb) info functions uptime All functions matching regular expression "uptime": Non-debugging symbols: 0x000055caa45ceba0 Platform_getUptime
Looks like Platform_getUptime is what we’re looking for. Let’s add a breakpoint on that function call so we can see examine it’s return value:
(gdb) break Platform_getUptime Breakpoint 1 at 0x55caa45ceba0 (gdb) continue Continuing. Breakpoint 1, 0x000055caa45ceba0 in Platform_getUptime ()
Looks like we hit it immediately. Now let’s get it’s return value:
(gdb) finish Run till exit from #0 0x000055caa45ceba0 in Platform_getUptime () 0x000055caa45c654d in ?? () (gdb) print $rax $1 = 50689
NOTE: You may need to use the 32-bit $eax register instead. Also this may not work on some CPUs.
It returned the value 50689, which is roughly the number of seconds my machine has been on.
As a proof of concept, let’s return the value 1 directly and see what that does.
(gdb) return (int)1 Make selected stack frame return now? (y or n) y #0 0x000055caa45c654d in ?? () (gdb) continue Continuing. Breakpoint 1, 0x000055caa45ceba0 in Platform_getUptime ()
We’ve come back around to our breakpoint and you should now see an uptime of 00:00:01 in htop .
At this point we have enough information to write our hook.
Code is short and well documented, but essentially we’re using the subhook library to hook htop ‘s Platform_getUptime() function so that it always returns 0. This results in the htop process always displaying an uptime value or 0.
A very simple script to find the PID of a running process by name and inject a shared library.
We are injecting our hook using the pyinjector python library which is a wrapper around kubo/injector.
You’ll need a C compiler, cmake, python + pip, and htop .
Don’t forget to clone recursively to pull in the subhook dependency.
git clone --recursive https://github.com/edouardpoitras/process_injection_example.git
Also need the python dependency:
The Linux kernel makes this possible through the use of ptrace. Because this is a rather sketchy thing to do usually only performed by debuggers, anti-virus’, or malicious actors, the Linux Security Module (yama) will give you a hard time unless your system is configured to allow it.
You can temporarily disable the ptrace restriction (until reboot) by modifying the ptrace_scope value:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
More information can be found here.
Ensure htop is running in a terminal on your machine, then inject the hook with the command:
python inject.py htop libhtop_hook.so
You should notice the uptime value change to 00:00:00 .
About
Linux Remote Process Injection and Hooking Example — htop
How do I run command hooks when a specific process is started?
Once you have the PID, what will you do with «process A»? Will you it once per «process A», or repeatedly. Do you have the support of your SysAdmin AND the owner of «process A»? If we help, will it turn out to have been criminal?
@icarus imagine a client-server arch where multiple clients can run arbitrary programs on the server. The server don’t know when the program will be run by the client (nor what programs will be run by them), but when a certain program is run the server wants to run additional scripts
@waltinator No, we will not modify Process A. Instead, we have another secure process running on the server that we control (eg: Process B) that provides data for other processes. In order for any process to access the data, they need to be authenticated with Process B which utilizes the proc PID. So in this case, we want to authenticate all instances of Process A runs (cos they can be run multiple times with different PIDs) with Process B.
@icarus Yes, we’re the admin of the server machine (where Process A is run) but not the admin of the client machines. Process A is a process that we can’t modify and can’t know when they start as it is triggered from the client-side, but the server has control over stopping them.
1 Answer 1
The only reliable solution is to arrange to run the program via a wrapper script. There are many different ways to do that, and it’s unusual for none of them to apply.
- Configure or modify the program(s) that call this program to run your wrapper instead.
- Put a wrapper by the same name ahead of the real program in the $PATH .
- Rename the program and give the wrapper its original name.
This approach guarantees that your wrapper will run, and it gets the opportunity to prepare before running the real program if necessary. The wrapper also gets the ability to be the parent process of the real program, which lets it reliably be notified when it dies and obtain its exit status.
You can use LoggedFS to monitor events on a filesystem. See LoggedFS configuration file syntax for documentation on its configuration file. Point loggedfs to the directory containing the executable. Note that you need to own it or run loggedfs as root. Each time a file is executed, there is an open event, and you get the PID of the process. Note that there is also an open event if the file is opened for reading; I don’t think you can distinguish execute events specifically with LoggedFS alone. When the process exits, there is a release event. Note that by the time your monitoring code runs, the process might have already died.
Under Linux, you can use inotifywait to wait for an access or close_nowrite event on the executable, e.g. inotifywait -m -e access,close_nowrite —format=%e /bin/ls . There is an access event whenever the file is executed and a close_nowrite when the process dies. You can’t get the process ID that way, so you’ll then have to find out which processes have the file open (e.g. with fuser or lsof ) and then filter the ones that are executing the file.
Linux also has a more precise facility that lets you monitor events such as the execution of a specific file and reports the PID that caused the event: the audit subsystem. See Is there an easy way to log all commands executed, including command line arguments?. You can then use audispd to run a custom program when the audit event occurs. All this requires being root, though, and if you were root then presumably you could replace the executable and you wouldn’t need this.
Hook action on process creation
Is it possible to hook a script execution on each process creation? Essentially the equivalent of inotifywait to monitor disk activity but applied to the process table. It would be to allow to do an action upon spawning of the processes, for example logging it, cgset it, other. I can see the challenge that it would recursively apply on the new processes. But instead of polling the process table as fast as possible to catch changes which would be vulnerable to race conditions, is there a better way. Thanks
No. If you did that, creating a process would trigger the script which would trigger the script which would trigger the script… What problem are you actually trying to solve?
@Gilles: Thanks for your comment, I updated my question. It would be to allow to do an action upon spawning of the processes, for example logging it.
2 Answers 2
First, process creation is rarely a useful event to log and it’s irrelevant for security (except for resource limiting). I think you mean to hook the execution of programs, which is done by execve , not fork .
Second, the use cases you cite are usually best served by using existing mechanism made for that purpose, rather than rolling your own.
- For logging, BSD process accounting provides a small amount of information, and is available on most Unix variants; on Linux, install the GNU accounting utilities (install the package from your distribution). For more sophisticated logging on Linux, you can use the audit subsystem (the auditctl man page has examples; as I explained above the system call you’ll want to log is execve ).
- If you want to apply security restrictions to certain programs, use a security framework such as SELinux or AppArmor.
- If you want to run a specific program in a container, or with certain settings, move the executable and put a wrapper script in its place that sets the settings you want and calls the original executable.
If you want to modify the way one specific program calls other programs, without affecting how other programs behave, there are two cases: either the program is potentially hostile or not.
- If the program is potentially hostile, run it in a dedicated virtual machine.
- If the program is cooperative, the most obvious angle of attack is to run it with a different PATH . If the program uses absolute paths that aren’t easy to configure, on a non-antique Linux system, you can run it in a separate mount namespace (see also kernel: Namespaces support). If you really need fine control, you can load a library that overrides some library calls by invoking the program with LD_PRELOAD=my_override_library.so theprogram . See Redirect a file descriptor before execution for an example. Note that in addition to execve , you’ll need to override all the C library functions that call execve internally, because LD_PRELOAD doesn’t affect internal C library calls. You can get more precise control by running the program under ptrace ; this allows you to override a system call even if it’s made by a C library function, but it’s harder to set up (I don’t know of any easy way to do it).