In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/01 Report--
This article will explain in detail how to use Ptrace to intercept and simulate Linux system calls. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.
Ptrace (2) ("process tracking process trace") system calls are usually related to debugging. It is the main mechanism for monitoring debugged processes through native debuggers on Unix-like systems. It is also a common way to implement strace (system call tracking system call trace). With Ptrace, the tracker can pause the tracked process, check and set registers and memory, monitor system calls, and even intercept intercepting system calls.
Through the interception function, it means that the tracker can tamper with system call parameters, tamper with the return value of system calls, and even block some system calls. The implication is that a tracker itself can provide a system call service. This is interesting because it means that a tracker can emulate a complete external operating system, which is implemented by Ptrace without any help from the kernel.
The problem is that only one tracker can be attached to a process at a time, so it is no longer possible to use a tool such as GDB to emulate an external operating system during debugging of that process. Another problem is that the cost of simulating system calls is very high.
Strace
Before moving on to the most interesting part, let's start by reviewing the basic implementation of strace. It's not DTrace, but strace is still very useful.
Ptrace has never been standardized. Its interfaces are very similar on different operating systems, especially in terms of core functions, but there are still slight differences between different systems. The prototype of ptrace (2) should basically look like this, but there may be some differences in specific types.
Long ptrace (int request, pid_t pid, void * addr, void * data)
Pid is the ID of the process being tracked. Although only one tracker can be attached to the process at a time, a tracker can attach and track multiple processes.
The request field selects a specific Ptrace function, such as the ioctl (2) interface. For strace, only two are required:
PTRACE_TRACEME: this process is tracked by its parent process.
PTRACE_SYSCALL: continue to trace, but stop at the next system call entry or exit.
PTRACE_GETREGS: gets a copy of the register contents of the tracked process.
The other two fields, addr and data, are used as general parameters for the selected Ptrace function. In general, one or all of them can be ignored, in which case zero parameters are passed.
The strace interface is essentially prefixed to another command.
$strace [strace options] program [arguments]
The minimized strace does not require any options, so the * * thing you need to do-assuming it has at least one parameter-is to track the process at fork (2) and exec (2) at the end of the argv. But before loading the target program, the new process will tell the kernel that the target program will continue to be tracked by its parent process. The tracked process will be paused by this Ptrace system call.
Pid_t pid = fork (); switch (pid) {case-1: / * error * / FATAL ("% s", strerror (errno)); case 0: / * child * / ptrace (PTRACE_TRACEME, 0,0,0); execvp (argv [1], argv + 1); FATAL ("% s", strerror (errno));}
The parent process uses wait (2) to wait for the PTRACE_TRACEME of the child process, and when wait (2) returns, the child process is paused.
Waitpid (pid, 0,0)
Before allowing the child process to continue, we tell the operating system that the tracked process and its parent process should be terminated together. A real strace implementation may set other options, such as PTRACE_O_TRACEFORK.
Ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL)
The rest is a simple, endless loop that captures one system call at a time. The body of the loop has a total of four steps:
Wait for the process to proceed to the next system call.
Outputs a description of the system call.
Allows the system call to run and wait for a return.
Output the system call return value.
This PTRACE_SYSCALL request is used to wait for the next system call to start and for that system call to exit. As before, a wait (2) is needed to wait for the tracked process to enter the desired state.
Ptrace (PTRACE_SYSCALL, pid, 0,0); waitpid (pid, 0,0)
When wait (2) returns, the system call number and its parameters are written in the register of the thread that made the system call. However, the operating system still does not provide services for this system call. This detail is important for subsequent operations.
The next step is to collect system call information. This is where the architecture of each system is different. On x86-64, system call numbers are passed in rax, while parameters (up to 6) are passed in rdi, rsi, rdx, R10, R8, and R9. These registers are read by another Ptrace call, but wait (2) is no longer needed here, because the state of the tracked process will never change again.
Struct user_regs_struct regs;ptrace (PTRACE_GETREGS, pid, 0, & regs); long syscall = regs.orig_rax; fprintf (stderr, "% ld (% ld,% ld)", syscall, (long) regs.rdi, (long) regs.rsi, (long) regs.rdx, (long) regs.r10, (long) regs.r8, (long) regs.r9)
Here's a warning. Because of the internal use of the kernel, the system call number is stored in orig_rax rather than rax. All other system call parameters are very simple and straightforward.
Next comes its other PTRACE_SYSCALL and wait (2), and then another PTRACE_GETREGS to get the results. The results are saved in rax.
Ptrace (PTRACE_GETREGS, pid, 0, & regs); fprintf (stderr, "=% ld\ n", (long) regs.rax)
The output of this simple program is also very rough. None of the system calls here have a symbolic name, and all parameters are output digitally, even a pointer to the buffer. The more complete strace output will know which parameters are pointers and use process_vm_readv (2) to read which buffers from the tracked process in order to output them correctly.
However, these are only the basics of system call interception.
System call interception
Suppose we want to use Ptrace to implement functions such as OpenBSD's pledge (2), which is a process that promises pledge to use only a limited set of system calls. The initial idea is that many programs generally have an initialization phase, which requires a lot of system access (for example, opening files, binding sockets, and so on). After initialization, they perform a main loop in which they process input and use only a small set of system calls that are needed.
Before entering the main loop, a process can limit itself to only a few operations it needs. If the program is flawed, it can be exploited through malicious input, and this commitment can effectively limit the implementation of vulnerability exploitation.
Using the same model as strace, but not outputting all system calls, we can either block some system calls or simply terminate the tracked process if its behavior is abnormal. Terminating it is easy: just call exit (2) in the tracker. Therefore, it can also be set to terminate the tracked process. Blocking system calls and allowing child processes to continue running are just tricks.
The trickiest part is that there is no way to interrupt the system call after it is started. When the tracker returns to the system call from wait (2) at the entry, the only way to stop a system call from the beginning is to terminate the tracked process.
However, we can not only "mess up" the parameters of the system call, but also change the system call number itself to a system call that does not exist. When returned, we can report a "friendly" error message through the normal internal signal in the errno.
For (;;) {/ * Enter next system call * / ptrace (PTRACE_SYSCALL, pid, 0,0); waitpid (pid, 0,0); struct user_regs_struct regs; ptrace (PTRACE_GETREGS, pid, 0, & regs); / * Is this system call permitted? * / int blocked = 0; if (is_syscall_blocked (regs.orig_rax)) {blocked = 1; regs.orig_rax =-1 / / set to invalid syscall ptrace (PTRACE_SETREGS, pid, 0, & regs);} / * Run system call and stop on exit * / ptrace (PTRACE_SYSCALL, pid, 0,0); waitpid (pid, 0,0); if (blocked) {/ * errno = EPERM * / regs.rax =-EPERM; / / Operation not permitted ptrace (PTRACE_SETREGS, pid, 0, & regs);}}
This simple example simply checks whether the system call violates the whitelist or blacklist. There is no difference here, for example, allowing files to be opened as read-only rather than read-write (open (2)), anonymous memory mapping but not non-anonymous mapping, and so on. But there is still no way to dynamically revoke the permissions of the tracked process.
How does the tracker communicate with the process being tracked? Use artificial system calls!
Create an artificial system call
For my pledge-like system call-- I can distinguish it from the real system call by calling xpledge ()-- I set 10000 as its system call number, which is a very large number that is never used in real system calls.
# define SYS_xpledge 10000
For demonstration purposes, I also built a very small interface, which is not a good idea in practice. It bears some resemblance to OpenBSD's pledge (2) in that it uses a string interface. In fact, designing a robust and secure permission set is very complex, as shown on the manual page of pledge (2). The following is the complete interface and implementation of the system call to the tracked process:
# define _ GNU_SOURCE#include # define XPLEDGE_RDWR (1
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 224
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.