Snippets
Created by
Bart van Strien
last modified
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | /* Copyright (c) 2016, Bart van Strien
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
// Compile using:
// gcc -shared -fPIC -o normal-intercept.so normal-intercept.c -ldl
// Inject using:
// LD_PRELOAD=./normal-intercept.so ./some.binary
// For dlsym and RTLD_NEXT
#define _GNU_SOURCE
#include <dlfcn.h>
// For open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// For varags
#include <stdarg.h>
// For strcat
#include <string.h>
// For malloc
#include <stdlib.h>
// For printf, so this could be removed in an actual production version
#include <stdio.h>
static int (*real_open)(const char *pathname, int flags, ...);
static FILE *(*real_fopen)(const char *path, const char *mode);
// At loadtime, resolve our pointer to real_open
static void __attribute__((constructor)) init_hook()
{
real_open = dlsym(RTLD_NEXT, "open");
real_fopen = dlsym(RTLD_NEXT, "fopen");
}
int open(const char *pathname, int flags, ...)
{
int ret;
char *mypathname = NULL;
fprintf(stderr, "Open call for '%s'\n", pathname);
// Do some redirecting here
if (pathname[0] == '/') // For example, make everything relative
{
mypathname = malloc(strlen(pathname)+2);
mypathname[0] = '.';
mypathname[1] = 0;
strcat(mypathname, pathname);
}
// If we did a redirect, replace pathname
if (mypathname)
{
fprintf(stderr, "Redirected to '%s'\n", mypathname);
pathname = mypathname;
}
// Deal with the optional mode flag
if ((flags & O_CREAT) || (flags & O_TMPFILE))
{
va_list args;
va_start(args, flags);
mode_t mode = va_arg(args, mode_t);
va_end(args);
ret = real_open(pathname, flags, mode);
}
else
ret = real_open(pathname, flags);
// If we allocated our own string, free it here
if (mypathname)
free(mypathname);
// Now return whatever open returned
return ret;
}
// Something similar for fopen
FILE *fopen(const char *path, const char *mode)
{
fprintf(stderr, "Fopen call for '%s'\n", path);
return real_fopen(path, mode);
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* Copyright (c) 2016, Bart van Strien
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
// Compile using:
// gcc -shared -fPIC -o ptrace-intercept.so ptrace-intercept.c
// Inject using:
// LD_PRELOAD=./ptrace-intercept.so ./some.binary
#include <sys/ptrace.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <linux/limits.h>
#ifdef __x86_64
# define REGISTER_STRUCT_TYPE struct user_regs_struct
# define GET_SYSCALL(regs) ((regs).orig_rax)
# define SYSCALL_ARG1(regs) ((regs).rdi)
#elif __ARM_EABI__
# define REGISTER_STRUCT_TYPE struct user_regs
# define GET_SYSCALL(regs) ((regs).uregs[7])
# define SYSCALL_ARG1(regs) ((regs).uregs[0])
#else
# error Unknown architecture
#endif
#define sharedMemSize PATH_MAX+2
static char *sharedMem;
// Our "host" function
static __attribute__((noreturn)) void watch(int pid);
static void __attribute__((constructor)) init_hook()
{
// Allocate our shared memory
sharedMem = mmap(NULL, sharedMemSize, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0);
// Now fork, and let the parent trace the child
int pid = fork();
if (pid)
watch(pid);
else
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
kill(getpid(), SIGTRAP);
}
}
// Extract a string from the target's location
static const char *readstr(int pid, long *location)
{
static char buffer[sharedMemSize];
bool done = false;
for (int len = 0; len < sharedMemSize-sizeof(long)+1 && !done; len += sizeof(long))
{
long *target = (long*) &buffer[len];
*target = ptrace(PTRACE_PEEKTEXT, pid, location, NULL);
++location;
for (int i = len; i < len+sizeof(long); i++)
if (!buffer[i])
{
done = true;
break;
}
}
return buffer;
}
// Take a string, and return a string (in sharedMem) if it needs to be remapped
static const char *remap(const char *target)
{
// Remap any open("cake", ...) calls to open("pie", ...)
if (!strcmp(target, "cake"))
{
snprintf(sharedMem, sharedMemSize, "pie");
fprintf(stderr, "Remapping '%s' to '%s'\n", target, sharedMem);
return sharedMem;
}
return NULL;
}
// Our tracing parent process
static void watch(int pid)
{
int status;
wait(&status); // Wait for TRACEME
REGISTER_STRUCT_TYPE registers;
bool enter = true;
while (true)
{
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
wait(&status);
// If our child exited, so do we.
if (WIFEXITED(status))
break;
// If the target received a signal, instead of being trapped
if (WSTOPSIG(status) != SIGTRAP)
continue;
// Read the registers
ptrace(PTRACE_GETREGS, pid, NULL, ®isters);
// NOTE: change register names depending on cpu type
int syscall = GET_SYSCALL(registers);
// Or with other syscalls
if (syscall == SYS_open && enter)
{
const char *targetfile = readstr(pid, (long*) SYSCALL_ARG1(registers));
const char *remapfile = remap(targetfile);
if (remapfile)
{
SYSCALL_ARG1(registers) = (intptr_t) remapfile;
ptrace(PTRACE_SETREGS, pid, NULL, ®isters);
}
}
enter = !enter;
}
exit(0);
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | /* Copyright (c) 2016, Bart van Strien
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
// Compile using:
// gcc -shared -fPIC -o ptrace-intercept.so ptrace-intercept.c
// Inject using:
// LD_PRELOAD=./ptrace-intercept.so ./some.binary
#include <sys/ptrace.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <linux/limits.h>
#include <seccomp.h>
#ifdef __x86_64
# define REGISTER_STRUCT_TYPE struct user_regs_struct
# define GET_SYSCALL(regs) ((regs).orig_rax)
# define SYSCALL_ARG1(regs) ((regs).rdi)
#elif __ARM_EABI__
# define REGISTER_STRUCT_TYPE struct user_regs
# define GET_SYSCALL(regs) ((regs).uregs[7])
# define SYSCALL_ARG1(regs) ((regs).uregs[0])
#else
# error Unknown architecture
#endif
static char *sharedMem;
// Take a string, and return a string (in sharedMem) if it needs to be remapped
static const char *remap(const char *target)
{
// Remap any open("cake", ...) calls to open("pie", ...)
if (!strcmp(target, "cake"))
{
snprintf(sharedMem, PATH_MAX, "pie");
fprintf(stderr, "Remapping '%s' to '%s'\n", target, sharedMem);
return sharedMem;
}
return NULL;
}
// Extract a string from the target's location
static const char *readstr(int pid, long *location)
{
static char buffer[PATH_MAX+1];
bool done = false;
for (int len = 0; len < PATH_MAX-sizeof(long)+1 && !done; len += sizeof(long))
{
long *target = (long*) &buffer[len];
*target = ptrace(PTRACE_PEEKTEXT, pid, location, NULL);
++location;
for (int i = len; i < len+sizeof(long); i++)
if (!buffer[i])
{
done = true;
break;
}
}
return buffer;
}
// Our tracing parent process
static __attribute__((noreturn)) void watch(int pid)
{
int status;
wait(&status); // Wait for TRACEME
REGISTER_STRUCT_TYPE registers;
// Now set up listening for seccomp blocks
ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP);
while (true)
{
ptrace(PTRACE_CONT, pid, NULL, NULL);
wait(&status);
// If our child exited, so do we.
if (WIFEXITED(status))
break;
// If we didn't stop because of seccomp, continue
if ((status >> 8) != (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)))
continue;
// Read the registers
ptrace(PTRACE_GETREGS, pid, NULL, ®isters);
// NOTE: change register names depending on cpu type
int syscall = GET_SYSCALL(registers);
// Or with other syscalls
if (syscall == SYS_open)
{
const char *targetfile = readstr(pid, (long*) SYSCALL_ARG1(registers));
const char *remapfile = remap(targetfile);
if (remapfile)
{
SYSCALL_ARG1(registers) = (intptr_t) remapfile;
ptrace(PTRACE_SETREGS, pid, NULL, ®isters);
}
}
}
exit(0);
}
static void installFilter()
{
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
int rc = seccomp_rule_add(ctx, SCMP_ACT_TRACE(0), SCMP_SYS(open), 0);
if (rc >= 0)
rc = seccomp_load(ctx);
seccomp_release(ctx);
if (rc < 0)
{
fprintf(stderr, "Failed to load seccomp filter: %s\n", strerror(-rc));
exit(1);
}
}
static void __attribute__((constructor)) init_hook()
{
// Allocate our shared memory
sharedMem = mmap(NULL, PATH_MAX+1, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0);
// Now fork, and let the parent trace the child
int pid = fork();
if (pid)
watch(pid);
else
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
installFilter();
raise(SIGTRAP);
}
}
|