{"id":199,"date":"2023-08-25T13:21:00","date_gmt":"2023-08-25T13:21:00","guid":{"rendered":"https:\/\/thebrokenpipe.com\/blog\/?p=199"},"modified":"2026-04-26T11:44:42","modified_gmt":"2026-04-26T11:44:42","slug":"copying-execute-only-binaries-on-linux","status":"publish","type":"post","link":"https:\/\/thebrokenpipe.com\/blog\/copying-execute-only-binaries-on-linux\/","title":{"rendered":"Copying Execute-Only Binaries on Linux"},"content":{"rendered":"\n<p>I recently completed a C programming course at university. It was fun :). I guess the most interesting part was stealing the assignment <em>solutions<\/em>. Okay, just to be clear, I didn&#8217;t cheat.<\/p>\n\n\n\n<p>Basically, we had programming assignments, and the lecturers provided compiled assignment solutions as demos for us to test our implementations against. When I heard that compiled solution demos would be given along with the assignments, I got pretty excited, as I had a few years of experience reverse engineering C code.<\/p>\n\n\n\n<p>I asked one of the lecturers for permission to &#8220;steal&#8221; those demos and reverse engineer them, and I was given the go-ahead, which surprised me a bit. His original words were &#8220;we&#8217;ve made sure that you can&#8217;t just copy the executables and feed them into a reverse compiler, and if you are still able to do it, then there&#8217;s nothing much we can teach you in this course, but sure, you can try&#8221;. What could they have possibly done to protect their binaries?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Execute-Only Permissions<\/h2>\n\n\n\n<p>Binaries can be made execute-only, so that you only have execute permissions but not read or write permissions. Here&#8217;s an example of what an execute-only binary looks like:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>-rwx--x--x 1 root  root  15776 Aug 21 11:13 test<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">-rwx--x--x 1 root  root  15776 Aug 21 11:13 test<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<!--more-->\n\n\n\n<p>The permission bits can be divided up into 4 chunks and in this case, they are <code>-<\/code>, <code>rwx<\/code>, <code>--x<\/code> and <code>--x<\/code>. The first chunk, <code>-<\/code> is the file type, <code>d<\/code> means directory and <code>-<\/code> means regular file. The second chunk, <code>rwx<\/code> is the owner permissions, and in the case of my example, it&#8217;s the permission that <code>root<\/code> has (root always has full access anyway). The third chunk, <code>--x<\/code> is the group permissions, and in the above example, it&#8217;s the permissions that people in the group <code>root<\/code> have. The last chunk, <code>--x<\/code> is the other permissions, the permissions that everyone else not <code>root<\/code> and not in the group <code>root<\/code> have. If I&#8217;m not root, then I only have permission to execute the binary.<\/p>\n\n\n\n<p>This extends to almost every aspect of dealing with the binary, for example, you cannot load it in debuggers like <code>gdb<\/code> or attach a debugger to it after it is executed. While it&#8217;s running, you also don&#8217;t have read access to the virtual <code>\/proc\/$pid\/mem<\/code> file, to prevent you from dumping the binary while it&#8217;s running.<\/p>\n\n\n\n<p>Say I&#8217;m running the execute-only binary <code>test<\/code>, I can find its process ID:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>epipe@HP-Laptop:~$ ps\n    PID TTY          TIME CMD\n 317245 pts\/0    00:00:00 bash\n 317329 pts\/0    00:00:01 test\n 317344 pts\/0    00:00:00 ps<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">epipe@HP-Laptop:~$ ps<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">    PID TTY          TIME CMD<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\"> 317245 pts\/0    00:00:00 bash<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\"> 317329 pts\/0    00:00:01 test<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\"> 317344 pts\/0    00:00:00 ps<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Then I could list its files:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>epipe@HP-Laptop:~$ ls -l \/proc\/317329\/maps \/proc\/317329\/mem\n-r--r--r-- 1 root root 0 Aug 21 11:28 \/proc\/317329\/maps\n-rw------- 1 root root 0 Aug 21 11:28 \/proc\/317329\/mem<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">epipe@HP-Laptop:~$ ls -l \/proc\/317329\/maps \/proc\/317329\/mem<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">-r--r--r-- 1 root root 0 Aug 21 11:28 \/proc\/317329\/maps<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">-rw------- 1 root root 0 Aug 21 11:28 \/proc\/317329\/mem<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The file <code>mem<\/code> could only be read by the owner (<code>root<\/code>), to prevent you from dumping it that way. You don&#8217;t even get to access the memory map, <code>maps<\/code>, despite having read permissions.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>epipe@HP-Laptop:~$ cat \/proc\/317329\/maps\ncat: \/proc\/317329\/maps: Permission denied<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">epipe@HP-Laptop:~$ cat \/proc\/317329\/maps<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">cat: \/proc\/317329\/maps: Permission denied<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>I eventually gave up trying to copy\/dump the binary and did the assignment myself. But, just a week before it was due, we had a lecture on file permissions and one of the lecturers talked about how Linux permissions were used to prevent students from stealing and decompiling the demo binaries, and that if anyone could bypass that, they&#8217;d get 100% on the assignment. The &#8220;get 100% on the assignment&#8221; part sparked my interest in stealing their demos again.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Existing Methods<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>ptrace<\/code> <code>PEEKTEXT<\/code> and <code>PEEKDATA<\/code><\/h3>\n\n\n\n<p>The most commonly documented way is to use <code>ptrace<\/code>. Basically, start the executable as a child process and then use <code>PTRACE_PEEKTEXT<\/code> and <code>PTRACE_PEEKDATA<\/code> to read its memory. This <a href=\"https:\/\/reverseengineering.stackexchange.com\/questions\/98\/how-can-i-analyse-an-executable-with-no-read-permission\/106#106\">Stack Exchange answer<\/a> gives a few examples, and there is even a tool called <a href=\"http:\/\/reverse.lostrealm.com\/tools\/xocopy.html\">XOCopy<\/a> written just for this.<\/p>\n\n\n\n<p>There are 2 problems with this approach:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>To use <code>ptrace<\/code>, you need to know the starting address and length of the content you&#8217;d like to dump. There are 3 ways suggested by people on the internet:\n<ul class=\"wp-block-list\">\n<li>Read <code>\/proc\/$pid\/maps<\/code>, but as seen earlier, you don&#8217;t have permission to read it<\/li>\n\n\n\n<li>Use <code>strace<\/code> to figure out where it starts executing, but you cannot guarantee that the binary is mapped at the same place each time, especially with ASLR<\/li>\n\n\n\n<li>Walk the entire address space scanning for ELF headers, but it&#8217;s next to impossible on 64-bit machines<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Now pretending that you somehow know where to start dumping and how long to dump, the biggest problem is <code>PTRACE_PEEKTEXT<\/code> and <code>PTRACE_PEEKDATA<\/code> don&#8217;t work on execute-only binaries, <strong>they always return permission error<\/strong>!<\/li>\n<\/ul>\n\n\n\n<p>Now judging by what people said ~10 years ago, this existing approach work<strong>ed<\/strong>, but my guess is things have changed in the last decade and every technique used by this method no longer works.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>LD_PRELOAD<\/code> Abuse<\/h3>\n\n\n\n<p>Another method is to abuse <code>LD_PRELOAD<\/code> to inject some code to have the program dump itself to disk as a readable file, as described in this <a href=\"https:\/\/unix.stackexchange.com\/questions\/435445\/how-to-copy-a-file-i-have-permission-to-execute-but-not-permission-to-copy\/711936#711936\">Stack Exchange answer<\/a> by <a href=\"https:\/\/unix.stackexchange.com\/users\/22565\/st%c3%a9phane-chazelas\">St\u00e9phane Chazelas<\/a>.<\/p>\n\n\n\n<p>This method works! It works because it can inject arbitrary code into a process at startup. As we know, the process itself can read its own memory and memory map, so there&#8217;s nobody stopping the process from dumping itself.<\/p>\n\n\n\n<p>While it works, there are a few downsides:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>While the code runs before hitting <code>main()<\/code>, the runtime has already made changes to global data<\/li>\n\n\n\n<li>It doesn&#8217;t work on statically linked executables<\/li>\n\n\n\n<li>It doesn&#8217;t work on SUID or SGID binaries, as injecting arbitrary code into processes with higher privileges is a security meltdown<\/li>\n\n\n\n<li>The dumped binary cannot be executed, as various variables have already been initialised<\/li>\n<\/ul>\n\n\n\n<p>Despite these downsides, I cobbled together a tool called <code>dynadump<\/code> based on this method and used it to dump the assignment 1 demo executable, which I then decompiled. In fact, I made the decompiled code compile back to the exact same binary. I told the course staff about it and they said they&#8217;d start doing more static linking.<\/p>\n\n\n\n<p>At the same time, a second assignment was released, and it was in the form of a client\/server, where students would invoke a client given to them and the client would send the student&#8217;s attempts to the server via a pipe. The server then checked if the answers were correct and logged the attempts. Because of this, the log files which contained the students&#8217; attempts and results must not be accessible by students, so they made the server SGID.<\/p>\n\n\n\n<p>I wanted to dump the server to figure out how the backend worked, however the <code>LD_PRELOAD<\/code> trick doesn&#8217;t work on SGID binaries. They also said they&#8217;d start doing static linking, so I thought I soon wouldn&#8217;t be able to dump assignment solution demos that way either. For these reasons, I started thinking of another way to dump execute-only binaries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Exploiting <code>printf(3)<\/code><\/h2>\n\n\n\n<p>The main thing about dumping execute-only binaries is to get the process to dump itself, and everything revolves around that. A process dumps itself via syscalls &#8211; at the very bottom layer, it creates a dump file using <code>creat(2)<\/code>, writes itself to that file using <code>write(2)<\/code>, and closes the file with <code>close(2)<\/code>.<\/p>\n\n\n\n<p>A lot of these syscalls existed in the binaries that I wanted to dump, for example a <code>printf(2)<\/code> probably ends up somewhere with a <code>write(2)<\/code> call. So, if I could catch that <code>write(2)<\/code> call, extend the length of the write, then it would print more than just the string it wanted to print and leak whatever is after the string in memory. If I then changed the pointer as well, I could start printing at an arbitrary address, for an arbitrary length, and this would effectively give me the ability to dump anything to the standard output.<\/p>\n\n\n\n<p>The real challenge lies in changing the parameters <code>buf<\/code> and <code>count<\/code> of a <code>write(2)<\/code> call. Luckily, while <code>ptrace(2)<\/code> blocks accessing the memory of an execute-only tracee, it does not block reading and modifying the tracee&#8217;s registers. In Linux, syscall parameters are passed via registers for most architectures, so stopping at a <code>write(2)<\/code> call, modifying the registers which hold <code>buf<\/code> and <code>count<\/code>, and continuing is sufficient.<\/p>\n\n\n\n<p>This method is not perfect, however. First, finding the starting address to dump &#8211; the ELF header, is not trivial. There are some heuristics for finding it, such as scanning from the original <code>buf<\/code> pointer minus the size of the executable, but it&#8217;s really just guesswork without a proper memory map. Then finding the right <code>write(2)<\/code> call to modify is also a challenge, an executable may not have any <code>write(2)<\/code> calls at all. Even if a correct <code>write(2)<\/code> call is found, by the time it gets there, you&#8217;re in the middle of running the program so the initialised global variables may have already been modified and lost their original values.<\/p>\n\n\n\n<p>Despite the limitations, I put together <code>writedump<\/code> which changed the <code>buf<\/code> and <code>count<\/code> parameters to <code>write(2)<\/code> and implemented some basic guessing of ELF header location, and used it to dump the SGID assignment 2 server backend.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Arbitrary Syscall Injection<\/h2>\n\n\n\n<p>A thought that later came up was, if the registers could be modified, why not just modify the syscall number and all the registers to execute an arbitrary syscall? This way, we don&#8217;t have to rely on a particular syscall like <code>write(2)<\/code>, and we can catch the very first syscall, before the program had a chance to mess with the global variables.<\/p>\n\n\n\n<p>I quickly cobbled together a function to perform arbitrary syscall injection:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>bool inject_syscall(pid_t pid,\n\tunsigned long rax,\n\tunsigned long rdi,\n\tunsigned long rsi,\n\tunsigned long rdx,\n\tunsigned long r10,\n\tunsigned long r8,\n\tunsigned long r9,\n\tunsigned long *result);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">bool<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">inject_syscall<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">pid_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">pid<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">rax<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">rdi<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">rsi<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">rdx<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">r10<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">r8<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">r9<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">result<\/span><span style=\"color: #002339\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The assumption is that when this function is called, the tracee has already been stopped at the beginning of a syscall, with <code>PTRACE_SYSCALL<\/code>. The logic (sans error handling) is like:<\/p>\n\n\n\n<p>1. Get the original registers:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct user_regs_struct orig_regs;\nptrace(PTRACE_GETREGS, pid, NULL, &amp;orig_regs);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">struct<\/span><span style=\"color: #002339\"> user_regs_struct orig_regs;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">ptrace<\/span><span style=\"color: #002339\">(PTRACE_GETREGS, pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">orig_regs<\/span><span style=\"color: #002339\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>2. Create new register set with manipulated registers:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>struct user_regs_struct temp_regs = orig_regs;\ntemp_regs.orig_rax = rax;\ntemp_regs.rdi = rdi;\ntemp_regs.rsi = rsi;\ntemp_regs.rdx = rdx;\ntemp_regs.r10 = r10;\ntemp_regs.r8 = r8;\ntemp_regs.r9 = r9;<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">struct<\/span><span style=\"color: #002339\"> user_regs_struct temp_regs <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> orig_regs;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.orig_rax <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> rax;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.rdi <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> rdi;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.rsi <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> rsi;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.rdx <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> rdx;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.r10 <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> r10;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.r8 <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> r8;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">temp_regs.r9 <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> r9;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>3. Replace original registers with manipulated ones:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>ptrace(PTRACE_SETREGS, pid, NULL, &amp;temp_regs);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #7EB233\">ptrace<\/span><span style=\"color: #002339\">(PTRACE_SETREGS, pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">temp_regs<\/span><span style=\"color: #002339\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>4. Execute the syscall and wait for it to finish:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>ptrace(PTRACE_SYSCALL, pid, NULL, NULL);\nwait_child(pid);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #7EB233\">ptrace<\/span><span style=\"color: #002339\">(PTRACE_SYSCALL, pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">wait_child<\/span><span style=\"color: #002339\">(pid);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>5. Get the return value of the syscall:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>ptrace(PTRACE_GETREGS, pid, NULL, &amp;temp_regs);\n*result = temp_regs.rax;<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #7EB233\">ptrace<\/span><span style=\"color: #002339\">(PTRACE_GETREGS, pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">temp_regs<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">result <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> temp_regs.rax;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>6. Restore original registers and syscall number:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>orig_regs.rip -= 2; \/\/ Step back to before syscall instruction.\norig_regs.rax = orig_regs.orig_rax;\nptrace(PTRACE_SETREGS, pid, NULL, &amp;orig_regs);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">orig_regs.rip <\/span><span style=\"color: #7B30D0\">-=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #174781\">2<\/span><span style=\"color: #002339\">;<\/span><span style=\"color: #357B42; font-style: italic\"> \/\/ Step back to before syscall instruction.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">orig_regs.rax <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> orig_regs.orig_rax;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">ptrace<\/span><span style=\"color: #002339\">(PTRACE_SETREGS, pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">orig_regs<\/span><span style=\"color: #002339\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>7. Break at the current syscall again:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>ptrace(PTRACE_SYSCALL, pid, NULL, NULL);\nwait_child(pid);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #7EB233\">ptrace<\/span><span style=\"color: #002339\">(PTRACE_SYSCALL, pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">wait_child<\/span><span style=\"color: #002339\">(pid);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This function then serves as the basis for syscall wrappers. I created wrappers for each syscall, like this:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>ssize_t inject_write(pid_t pid, int fd, const void *buf, size_t count)\n{\n\tunsigned long result = 0;\n\tif (inject_syscall(pid, 1, fd, (unsigned long)buf, (unsigned long)count,\n\t    0, 0, 0, &amp;result)) {\n\t\tif (result > -4096ULL) {\n\t\t\terrno = -(int)result;\n\t\t\treturn -1;\n\t\t}\n\t\treturn (ssize_t)result;\n\t} else {\n\t\terrno = 800;\n\t\treturn -1;\n\t}\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">ssize_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">inject_write<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">pid_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">pid<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">fd<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #DA5221\">const<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">buf<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">size_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">count<\/span><span style=\"color: #002339\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> result <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">if<\/span><span style=\"color: #002339\"> (<\/span><span style=\"color: #7EB233\">inject_syscall<\/span><span style=\"color: #002339\">(pid, <\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">, fd, (<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\">)buf, (<\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\">)count,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t    <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #002339\">result)) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7B30D0\">if<\/span><span style=\"color: #002339\"> (result <\/span><span style=\"color: #7B30D0\">&gt;<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">-<\/span><span style=\"color: #174781\">4096<\/span><span style=\"color: #7B30D0\">ULL<\/span><span style=\"color: #002339\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t\terrno <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">-<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\">)result;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">-<\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> (<\/span><span style=\"color: #0991B6\">ssize_t<\/span><span style=\"color: #002339\">)result;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t} <\/span><span style=\"color: #7B30D0\">else<\/span><span style=\"color: #002339\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\terrno <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #174781\">800<\/span><span style=\"color: #002339\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">-<\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Given that syscall injection fully restores the execution context to before the injection, multiple injections can be done in succession. This allows us to inject syscalls to open and read the memory map, so we know exactly what and where to dump. We can also create a proper dump file instead of writing to <code>stdout<\/code> or <code>stderr<\/code> and then redirecting to a file.<\/p>\n\n\n\n<p>A new challenge that arose was how to share information between the tracer and tracee &#8211; for example, we can read the memory map by injecting an <code>open(2)<\/code> and a <code>read(2)<\/code> call, but what&#8217;s the path to the memory map and where to read it to? The tracer will have to figure out the path, and put it into the tracee&#8217;s address space for the tracee to execute the <code>open(2)<\/code> syscall. The solution I came up with was to use <code>pipe(2)<\/code> and <code>mmap(2)<\/code> &#8211; have a pipe between the tracer and tracee for communication, and allocate memory in the tracee by injecting <code>mmap(2)<\/code> calls. For example, if the tracer wants to open the tracee&#8217;s <code>\/proc\/self\/maps<\/code>, it would:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Map a page in the tracee&#8217;s address space by injecting <code>mmap(2)<\/code><\/li>\n\n\n\n<li>Write the string <code>\"\/proc\/self\/maps\"<\/code> into the pipe<\/li>\n\n\n\n<li>Read the string from the pipe into the mapped page by injecting <code>read(2)<\/code><\/li>\n\n\n\n<li>Pass the address of the page into <code>open(2)<\/code> as the <code>path<\/code> parameter<\/li>\n\n\n\n<li>Unmap the page from the tracee&#8217;s address space with <code>munmap(2)<\/code><\/li>\n<\/ul>\n\n\n\n<p>Mapping (allocating) memory into the tracee is done with:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>void *child_alloc(pid_t pid, size_t size)\n{\n\tvoid *result = inject_mmap(pid, NULL, size, PROT_READ | PROT_WRITE |\n\t    PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);\n\treturn (result == (void *)-1) ? NULL : result;\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #7EB233\">child_alloc<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">pid_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">pid<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">size_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">size<\/span><span style=\"color: #002339\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">result <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">inject_mmap<\/span><span style=\"color: #002339\">(pid, <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\">, size, PROT_READ <\/span><span style=\"color: #7B30D0\">|<\/span><span style=\"color: #002339\"> PROT_WRITE <\/span><span style=\"color: #7B30D0\">|<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t    PROT_EXEC, MAP_PRIVATE <\/span><span style=\"color: #7B30D0\">|<\/span><span style=\"color: #002339\"> MAP_ANONYMOUS, <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> (result <\/span><span style=\"color: #7B30D0\">==<\/span><span style=\"color: #002339\"> (<\/span><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">)<\/span><span style=\"color: #7B30D0\">-<\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">) <\/span><span style=\"color: #7B30D0\">?<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #174781\">NULL<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">:<\/span><span style=\"color: #002339\"> result;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Copying memory from tracer to tracee (sans error handling):<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>void *parent_to_child(pid_t pid, int *fds, const void *buff, size_t length)\n{\n\tchar *chbuf = (char *)child_alloc(pid, length);\n\twhile (length) {\n\t\tsize_t chunk = PIPE_SIZE &lt; length ? PIPE_SIZE : length;\n\t\twrite(fds&#91;1&#93;, buff, chunk);\n\t\tbuff = (const char *)buff + chunk;\n\t\tinject_read(pid, fds&#91;0&#93;, chbuf, wr);\n\t\tchbuf += chunk;\n\t\tlength -= chunk;\n\t}\n\treturn chbuf;\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #7EB233\">parent_to_child<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">pid_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">pid<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">fds<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #DA5221\">const<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">buff<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">size_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">length<\/span><span style=\"color: #002339\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">chbuf <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> (<\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">)<\/span><span style=\"color: #7EB233\">child_alloc<\/span><span style=\"color: #002339\">(pid, length);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">while<\/span><span style=\"color: #002339\"> (length) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #0991B6\">size_t<\/span><span style=\"color: #002339\"> chunk <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> PIPE_SIZE <\/span><span style=\"color: #7B30D0\">&lt;<\/span><span style=\"color: #002339\"> length <\/span><span style=\"color: #7B30D0\">?<\/span><span style=\"color: #002339\"> PIPE_SIZE <\/span><span style=\"color: #7B30D0\">:<\/span><span style=\"color: #002339\"> length;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7EB233\">write<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #2F86D2\">fds<\/span><span style=\"color: #002339\">&#91;<\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">&#93;, buff, chunk);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\tbuff <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> (<\/span><span style=\"color: #DA5221\">const<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">)buff <\/span><span style=\"color: #7B30D0\">+<\/span><span style=\"color: #002339\"> chunk;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7EB233\">inject_read<\/span><span style=\"color: #002339\">(pid, <\/span><span style=\"color: #2F86D2\">fds<\/span><span style=\"color: #002339\">&#91;<\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">&#93;, chbuf, wr);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\tchbuf <\/span><span style=\"color: #7B30D0\">+=<\/span><span style=\"color: #002339\"> chunk;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\tlength <\/span><span style=\"color: #7B30D0\">-=<\/span><span style=\"color: #002339\"> chunk;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> chbuf;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>And the full code for calling <code>open(2)<\/code> from the tracer as the tracee:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>int child_open(pid_t pid, int *fds, const char *pathname, int flags, mode_t mode)\n{\n\tsize_t pathlen = strlen(pathname) + 1;\n\tchar *chbuf = parent_to_child(pid, fds, pathname, pathlen);\n\tint result = inject_open(pid, chbuf, flags, mode);\n\tchild_free(pid, chbuf, pathlen);\n\treturn result;\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">child_open<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">pid_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">pid<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">fds<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #DA5221\">const<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">pathname<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">flags<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">mode_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">mode<\/span><span style=\"color: #002339\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">size_t<\/span><span style=\"color: #002339\"> pathlen <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">strlen<\/span><span style=\"color: #002339\">(pathname) <\/span><span style=\"color: #7B30D0\">+<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">chbuf <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">parent_to_child<\/span><span style=\"color: #002339\">(pid, fds, pathname, pathlen);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> result <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">inject_open<\/span><span style=\"color: #002339\">(pid, chbuf, flags, mode);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7EB233\">child_free<\/span><span style=\"color: #002339\">(pid, chbuf, pathlen);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> result;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Other syscalls which require sharing of memory, like <code>read(2)<\/code>, are done in the same way. With them, the memory map of the tracee can be read:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>static char *get_mem_map(pid_t pid, int *fds)\n{\n\tint fd_map = child_open(pid, fds, \"\/proc\/self\/maps\", O_RDONLY, 0);\n\tchar *mapbuf = (char *)malloc(MAP_SIZE);\n\tssize_t bytesrd = child_read(pid, fds, fd_map, mapbuf, MAP_SIZE);\n\tinject_close(pid, fd_map);\n\tmapbuf&#91;bytesrd&#93; = '\\0';\n\treturn mapbuf;\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DA5221\">static<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #7EB233\">get_mem_map<\/span><span style=\"color: #002339\">(<\/span><span style=\"color: #0991B6\">pid_t<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #B1108E\">pid<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #B1108E\">fds<\/span><span style=\"color: #002339\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> fd_map <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">child_open<\/span><span style=\"color: #002339\">(pid, fds, <\/span><span style=\"color: #A44185\">&quot;\/proc\/self\/maps&quot;<\/span><span style=\"color: #002339\">, O_RDONLY, <\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">mapbuf <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> (<\/span><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">)<\/span><span style=\"color: #7EB233\">malloc<\/span><span style=\"color: #002339\">(MAP_SIZE);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">ssize_t<\/span><span style=\"color: #002339\"> bytesrd <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">child_read<\/span><span style=\"color: #002339\">(pid, fds, fd_map, mapbuf, MAP_SIZE);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7EB233\">inject_close<\/span><span style=\"color: #002339\">(pid, fd_map);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #2F86D2\">mapbuf<\/span><span style=\"color: #002339\">&#91;bytesrd&#93; <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #A44185\">&#39;<\/span><span style=\"color: #174781\">\\0<\/span><span style=\"color: #A44185\">&#39;<\/span><span style=\"color: #002339\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">return<\/span><span style=\"color: #002339\"> mapbuf;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Once we have the memory map, dumping the entire binary is just a matter of parsing the memory map and injecting <code>write(2)<\/code> for each section, like this:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>char *memmap = get_mem_map(pid, fds);\nint outfd = child_open(pid, fds, outfile, O_WRONLY, 00777);\nFILE *fpmap = fmemopen(memmap, strlen(memmap), \"r\");\nwhile((n = fscanf(fpmap, \"%p-%p%*s%lx%*s%*s%*&#91; &#93;%&#91;^\\n &#93;\\n\", (void **)&amp;start,\n    (void **)&amp;end, (long unsigned int *)&amp;offset, segpath)) != EOF) {\n\tif (n == 4 &amp;&amp; !strcmp(segpath, selfpath)) {\n\t\tinject_lseek(pid, outfd, offset, SEEK_SET);\n\t\tinject_write(pid, outfd, start, (size_t)(end - start));\n\t}\n}\nfclose(fpmap);\ninject_close(pid, outfd);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #0991B6\">char<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">memmap <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">get_mem_map<\/span><span style=\"color: #002339\">(pid, fds);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> outfd <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">child_open<\/span><span style=\"color: #002339\">(pid, fds, outfile, O_WRONLY, <\/span><span style=\"color: #7B30D0\">0<\/span><span style=\"color: #174781\">0777<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">FILE <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">fpmap <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">fmemopen<\/span><span style=\"color: #002339\">(memmap, <\/span><span style=\"color: #7EB233\">strlen<\/span><span style=\"color: #002339\">(memmap), <\/span><span style=\"color: #A44185\">&quot;r&quot;<\/span><span style=\"color: #002339\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7B30D0\">while<\/span><span style=\"color: #002339\">((n <\/span><span style=\"color: #7B30D0\">=<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">fscanf<\/span><span style=\"color: #002339\">(fpmap, <\/span><span style=\"color: #A44185\">&quot;<\/span><span style=\"color: #174781\">%p<\/span><span style=\"color: #A44185\">-<\/span><span style=\"color: #174781\">%p%*s%lx%*s%*s<\/span><span style=\"color: #207BB8; font-style: italic; font-weight: bold; text-decoration: underline\">%<\/span><span style=\"color: #A44185\">*&#91; &#93;<\/span><span style=\"color: #207BB8; font-style: italic; font-weight: bold; text-decoration: underline\">%<\/span><span style=\"color: #A44185\">&#91;^<\/span><span style=\"color: #174781\">\\n<\/span><span style=\"color: #A44185\"> &#93;<\/span><span style=\"color: #174781\">\\n<\/span><span style=\"color: #A44185\">&quot;<\/span><span style=\"color: #002339\">, (<\/span><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">**<\/span><span style=\"color: #002339\">)<\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">start<\/span><span style=\"color: #002339\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">    (<\/span><span style=\"color: #0991B6\">void<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">**<\/span><span style=\"color: #002339\">)<\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">end<\/span><span style=\"color: #002339\">, (<\/span><span style=\"color: #0991B6\">long<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">unsigned<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #0991B6\">int<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">*<\/span><span style=\"color: #002339\">)<\/span><span style=\"color: #7B30D0\">&amp;<\/span><span style=\"color: #B1108E\">offset<\/span><span style=\"color: #002339\">, segpath)) <\/span><span style=\"color: #7B30D0\">!=<\/span><span style=\"color: #002339\"> EOF) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">if<\/span><span style=\"color: #002339\"> (n <\/span><span style=\"color: #7B30D0\">==<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #174781\">4<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">&amp;&amp;<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7B30D0\">!<\/span><span style=\"color: #7EB233\">strcmp<\/span><span style=\"color: #002339\">(segpath, selfpath)) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7EB233\">inject_lseek<\/span><span style=\"color: #002339\">(pid, outfd, offset, SEEK_SET);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #7EB233\">inject_write<\/span><span style=\"color: #002339\">(pid, outfd, start, (<\/span><span style=\"color: #0991B6\">size_t<\/span><span style=\"color: #002339\">)(end <\/span><span style=\"color: #7B30D0\">-<\/span><span style=\"color: #002339\"> start));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">fclose<\/span><span style=\"color: #002339\">(fpmap);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">inject_close<\/span><span style=\"color: #002339\">(pid, outfd);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This way, a binary can be dumped as long as <code>ptrace(2)<\/code> is enabled, and the dumped binary can be executed just like the original. The full program &#8211; <code>ExeOnlyDump<\/code>, can be found here on GitHub: <a href=\"https:\/\/github.com\/TheBrokenPipe\/ExeOnlyDump\">https:\/\/github.com\/TheBrokenPipe\/ExeOnlyDump<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently completed a C programming course at university. It was fun :). I guess the most interesting part was stealing the assignment solutions. Okay, just to be clear, I didn&#8217;t cheat. Basically, we had programming assignments, and the lecturers provided compiled assignment solutions as demos for us to test our implementations against. When I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,6],"tags":[],"class_list":["post-199","post","type-post","status-publish","format-standard","hentry","category-linux","category-reverse-engineering"],"_links":{"self":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts\/199","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/comments?post=199"}],"version-history":[{"count":52,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts\/199\/revisions"}],"predecessor-version":[{"id":257,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts\/199\/revisions\/257"}],"wp:attachment":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/media?parent=199"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/categories?post=199"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/tags?post=199"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}