├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── cmake-build-multiarch.sh ├── cmake-build.sh ├── src ├── Makefile ├── crazyrealloc.c ├── dealarm.c ├── deexec.c ├── defork.c ├── depselect.c ├── deptrace.c ├── derand.c ├── desigact.c ├── desleep.c ├── desock.c ├── desock_dup.c ├── desrand.c ├── detime.c ├── deuid.c ├── ensock.c ├── eofkiller.c ├── getcanary.c ├── logging.c ├── logging.h ├── mallocwatch.c ├── nowrite.c ├── patch.c ├── setcanary.c ├── setstdin.c ├── startstop.c └── writeout.c └── test ├── .gitignore ├── Makefile ├── hello.c ├── nowrite.c ├── rand.c ├── realloc.c ├── run_tests.sh ├── setstdin_fread.c ├── setstdin_getc.c ├── setstdin_read.c ├── sleep.c ├── sock.c ├── time.c └── uid.c /.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | *.so 3 | *.o 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (Preeny) 3 | 4 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 5 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 7 | 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 9 | 10 | add_library(common-preeny STATIC src/logging.c) 11 | 12 | # Malloc 13 | add_library(crazyrealloc SHARED src/crazyrealloc.c) 14 | target_link_libraries(crazyrealloc common-preeny dl) 15 | add_library(mallocwatch SHARED src/mallocwatch.c) 16 | target_link_libraries(mallocwatch common-preeny dl) 17 | 18 | # Signals 19 | add_library(dealarm SHARED src/dealarm.c) 20 | target_link_libraries(dealarm common-preeny) 21 | add_library(desigact SHARED src/desigact.c) 22 | target_link_libraries(desigact common-preeny) 23 | add_library(startstop SHARED src/startstop.c) 24 | target_link_libraries(startstop common-preeny) 25 | 26 | # Defork 27 | add_library(defork SHARED src/defork.c) 28 | target_link_libraries(defork common-preeny) 29 | 30 | # Deptrace 31 | add_library(deptrace SHARED src/deptrace.c) 32 | target_link_libraries(deptrace common-preeny) 33 | 34 | # Sleep 35 | add_library(desleep SHARED src/desleep.c) 36 | target_link_libraries(desleep common-preeny) 37 | 38 | # Socket 39 | add_library(desock SHARED src/desock.c) 40 | target_link_libraries(desock common-preeny dl pthread) 41 | add_library(desock_dup SHARED src/desock_dup.c) 42 | target_link_libraries(desock_dup common-preeny dl) 43 | add_library(ensock SHARED src/ensock.c) 44 | target_link_libraries(ensock common-preeny dl) 45 | add_library(writeout SHARED src/writeout.c) 46 | target_link_libraries(writeout common-preeny dl) 47 | 48 | # Random 49 | add_library(desrand SHARED src/desrand.c) 50 | target_link_libraries(desrand common-preeny dl) 51 | add_library(derand SHARED src/derand.c) 52 | target_link_libraries(derand common-preeny dl) 53 | 54 | # Time 55 | add_library(detime SHARED src/detime.c) 56 | target_link_libraries(detime common-preeny dl) 57 | 58 | # UID 59 | add_library(deuid SHARED src/deuid.c) 60 | target_link_libraries(deuid common-preeny dl) 61 | 62 | # Patch 63 | add_library(patch SHARED src/patch.c) 64 | target_link_libraries(patch common-preeny ini_config dl) 65 | 66 | # EOF killer 67 | add_library(eofkiller SHARED src/eofkiller.c) 68 | target_link_libraries(eofkiller common-preeny dl) 69 | 70 | # Canary 71 | add_library(getcanary SHARED src/getcanary.c) 72 | target_link_libraries(getcanary common-preeny dl) 73 | add_library(setcanary SHARED src/setcanary.c) 74 | target_link_libraries(setcanary common-preeny dl) 75 | 76 | # SetSTDIN 77 | add_library(setstdin SHARED src/setstdin.c) 78 | target_link_libraries(setstdin common-preeny dl) 79 | 80 | # nowrite 81 | add_library(nowrite SHARED src/nowrite.c) 82 | target_link_libraries(nowrite common-preeny dl) 83 | 84 | # Tests 85 | add_executable(test_hello test/hello.c) 86 | add_executable(test_rand test/rand.c) 87 | add_executable(test_realloc test/realloc.c) 88 | add_executable(test_sleep test/sleep.c) 89 | add_executable(test_sock test/sock.c) 90 | add_executable(test_uid test/uid.c) 91 | add_executable(test_setstdin_read test/setstdin_read.c) 92 | add_executable(test_setstdin_fread test/setstdin_fread.c) 93 | add_executable(test_setstdin_getc test/setstdin_getc.c) 94 | add_executable(test_nowrite test/nowrite.c) 95 | 96 | 97 | 98 | # Scripts 99 | file(COPY ${CMAKE_SOURCE_DIR}/test/run_tests.sh DESTINATION ${CMAKE_BINARY_DIR}) 100 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Yan Shoshitaishvili and others (https://github.com/zardus/preeny/graphs/contributors) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all dist test clean 2 | 3 | all: dist test 4 | 5 | dist: 6 | $(MAKE) -C src clean 7 | $(MAKE) -C src dist 8 | 9 | test: 10 | $(MAKE) -C test 11 | 12 | clean: 13 | $(MAKE) -C src clean 14 | $(MAKE) -C test clean 15 | rm -rf *-*-*/ 16 | 17 | archinfo: 18 | $(MAKE) -C src archinfo 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # preeny 2 | 3 | Preeny helps you pwn noobs by making it easier to interact with services locally. 4 | It disables `fork()`, `rand()`, and `alarm()` and, if you want, can convert a server application to a console one using clever/hackish tricks, and can even patch binaries! 5 | 6 | Preeny has the following modules: 7 | 8 | | Name | Summary | 9 | |------|---------| 10 | | dealarm | Disables alarm() | 11 | | defork | Disables fork() | 12 | | deptrace | Disables ptrace() | 13 | | derand | Disables rand() and random() | 14 | | desigact | Disables sigaction() | 15 | | desock | Channels socket communication to the console | 16 | | desock\_dup | Channels socket communication to the console (simpler method) | 17 | | ensock | The opposite of desock -- like an LD\_PRELOAD version of socat! | 18 | | desrand | Does tricky things with srand() to control randomness. | 19 | | detime | Makes time() always return the same value. | 20 | | desleep | Makes sleep() and usleep() do nothing. | 21 | | mallocwatch | When ltrace is inconvenient, mallocwatch provides info on heap operations. | 22 | | writeout | Some binaries write() to fd 0, expecting it to be a two-way socket. This makes that work (by redirecting to fd 1). | 23 | | patch | Patches programs at load time. | 24 | | startstop | Sends SIGSTOP to itself on startup, to suspend the process. | 25 | | crazyrealloc | ensures that whatever is being reallocated is always moved to a new location in memory, thus free()ing the old. | 26 | | deuid | Change the UID and effective UID of a process | 27 | | eofkiller | Exit on EOF on several read functions | 28 | | getcanary | Dumps the canary on program startup (x86 and amd64 only at the moment). | 29 | | setcanary | Overwrites the canary with a user-provided one on program startup (amd64-only at the moment). | 30 | | setstdin | Sets user defined STDIN data instead of real one, overriding `read`, `fread`, `fgetc`, `getc` and `getchar` calls. Read [here](#stdin-substitution) for more info | 31 | | nowrite | Forces open() to open files in readonly mode. Downgrading from readwrite or writeonly mode, and taking care of append, mktemp and other write-related flags as well | 32 | 33 | ## Building 34 | 35 | preeny's patch functionality uses `libini_config` to read `.ini` files. 36 | 37 | * On debian-based distros, you can install `libini-config-dev`. 38 | * On Arch-based distros, you can install `ding-libs`. 39 | * On Fedora-based distros, you can install `libini_config-devel`. 40 | 41 | Also deexec uses `seccomp` to setup a filter to blacklist `execve` like calls. 42 | 43 | * On debian-based distros, you can install `libseccomp-dev`. 44 | * On Arch-based distros, you can install `libseccomp`. 45 | * On Fedora-based distros, you can install `libseccomp-devel`. 46 | 47 | If you're not running a debian, Arch, or Fedora based distro, you've brought the pain upon yourself. 48 | 49 | You can build preeny by doing: 50 | 51 | make 52 | 53 | It'll create a directory named after the OS and architecture type, then put the libraries there. 54 | 55 | ### Cross-compilation 56 | 57 | If you need to build 32-bit x86 preeny libs on a 64-bit x86 host, you can do: 58 | 59 | make ARCH=i386 60 | 61 | Alternatively, if you want to utilize a cross-compiler, pass the `CC` variable to `make`. For example: 62 | 63 | make -i CC=mips-malta-linux-gnu-gcc 64 | 65 | Because some modules fail in cross-complilation, it's recommended to use `make -i`. 66 | 67 | ### CMake 68 | 69 | You can also build the project with cmake. Look at the cmake-build-*.sh scripts for example on how. 70 | 71 | ## Usage 72 | 73 | Let's say that you have an application that you want to interact with on the commandline, but it a) forks, b) sets an alarm which makes it hard to take your time studying its behavior, and c) demands to be connected to even if you don't want to do that. 74 | You can do: 75 | 76 | ```bash 77 | LD_PRELOAD=x86_64-linux-gnu/desock.so:x86_64-linux-gnu/defork.so:x86_64-linux-gnu/dealarm.so \ 78 | ~/code/security/codegate/2015/rodent/rodent 79 | ``` 80 | 81 | Pretty awesome stuff! Of course, you can pick and choose which preloads you want: 82 | 83 | ```bash 84 | echo 'No fork or alarm for you, but I still want to netcat!' 85 | LD_PRELOAD=x86_64-linux-gnu/defork.so:x86_64-linux-gnu/dealarm.so ~/code/security/codegate/2015/rodent/rodent 86 | 87 | echo 'Ok, go ahead and fork, but no alarm. Time to brute force that canary.' 88 | LD_PRELOAD=x86_64-linux-gnu/dealarm.so ~/code/security/codegate/2015/rodent/rodent 89 | ``` 90 | 91 | Have fun! 92 | 93 | ## Simple Things 94 | 95 | The simple functionality in preeny is disabling of fork and alarm. 96 | 97 | CTF services frequently use alarm to help mitigate hung connections from players, but this has the effect of being frustrating when you're debugging the service. 98 | 99 | Fork is sometimes frustrating because some tools are unable to follow fork on some platforms and, when they do follow fork, the parent is oftentimes abandoned in the background, needing to be terminated manually afterwards. 100 | 101 | `dealarm.so` replaces `alarm()` with a function that just does a `return 0`. 102 | `defork.so` does the same thing to `fork()`, means that the program will think that the fork has succeeded and that it's the child. 103 | 104 | ## Derandomization 105 | 106 | It's often easiest to test your exploits without extra randomness, and then ease up on the cheating little by little. 107 | Preeny ships with two modules to help: `derand` and `desrand`. 108 | 109 | `derand.so` replaces `rand()` and `random()` and returns a configurable value. Just specify it in the RAND environment (or go with the default of 42): 110 | 111 | ```bash 112 | # this will return 42 on each rand() call 113 | LD_PRELOAD=x86_64-linux-gnu/derand.so tests/rand 114 | 115 | # this will return 1337 on each rand() call 116 | RAND=1337 LD_PRELOAD=x86_64-linux-gnu/derand.so tests/rand 117 | ``` 118 | 119 | For slightly more complex things, `desrand.so` lets you override the `srand` function to your liking. 120 | 121 | ```bash 122 | # this simply sets the seed to 42 123 | LD_PRELOAD=x86_64-linux-gnu/desrand.so tests/rand 124 | 125 | # this sets the seed to 1337 126 | SEED=1337 LD_PRELOAD=x86_64-linux-gnu/desrand.so tests/rand 127 | 128 | # this sets the seed to such that the first "rand() % 128" will be 10 129 | WANT=10 MOD=128 LD_PRELOAD=x86_64-linux-gnu/desrand.so tests/rand 130 | 131 | # finally, this makes the *third* "rand() % 128" be 10 132 | SKIP=2 WANT=10 MOD=128 LD_PRELOAD=x86_64-linux-gnu/desrand.so tests/rand 133 | ``` 134 | 135 | `desrand` does all this by brute-forcing the seed value, so keep in mind that startup speed will get considerably slower as `MOD` increases. 136 | 137 | ## De-socketing 138 | 139 | Certain tools (such as American Fuzzy Lop, for example) are unable to handle network binaries. 140 | Preeny includes two "de-socketing" modules. 141 | `desock.so` neuters `socket()`, `bind()`, `listen()`, and `accept()`, making it return sockets that are, through hackish ways, synchronized to `stdin` and `stdout`. 142 | `desock_dup.so` is a simpler version for programs that dup accepted sockets over file descriptors 0, 1, and 2. 143 | 144 | A discussion of the different ways to de-socket program, and why Preeny does it the way it does, can be found [here](https://github.com/zardus/preeny/issues/10). 145 | 146 | ## En-socketing 147 | 148 | You can also use preeny to turn a normal binary into a socket binary! Just set the `PORT` environment variable (default is 1337) and preload `ensock.so`! 149 | 150 | ## Preload patching 151 | 152 | `patch.so` patches binaries! 153 | This is done before program start, by triggering the patcher from a constructor function in `patch.so`. 154 | Patches are specified in a `.ini` format, and applied by including `patch.so` in `LD_PRELOAD` and providing a patch file specified by the `PATCH` environment variable. 155 | For example: 156 | 157 | ```ShellSession 158 | # tests/hello 159 | Hello world! 160 | # cat hello.p 161 | [hello] 162 | address=0x4005c4 163 | content='4141414141' 164 | 165 | [world] 166 | address=0x4005ca 167 | content='6161616161' 168 | # PATCH="hello.p" LD_PRELOAD=x86_64-linux-gnu/patch.so tests/hello 169 | --- section hello in file hello.p specifies 5-byte patch at 0x4005c4 170 | --- section world in file hello.p specifies 5-byte patch at 0x4005ca 171 | AAAAA aaaaa! 172 | 173 | ``` 174 | 175 | Having different patch files and just enabling/disabling them via preload is oftentimes easier than modifying the underlying binary. 176 | 177 | ## STDIN substitution 178 | 179 | `setstdin.so` allows to replace `STDIN` with user defined data. It overrides `read`, `fread`, `fgetc`, `getc` and `getchar` calls, and 180 | return user defined data when binary asks for some `STDIN`. 181 | 182 | `setstdin` first tries to get user defined data form `PREENY_STDIN` environment variabe, if this variable is not defined, it tries to read data 183 | from file, defined in `PREENY_STDIN_FILENAME` environment variable. If both are not defined, `setstdin` uses some default value. 184 | 185 | ```ShellSession 186 | $ PREENY_STDIN=New_message LD_PRELOAD=src/setstdin.so test/setstdin_read 187 | N|ew|_me|ssag|e| 188 | 189 | $ echo "Some other message" > tmp_file 190 | $ PREENY_STDIN_FILENAME=tmp_file LD_PRELOAD=src/setstdin.so test/setstdin_getc 191 | S|o|m|e| |o|t|h|e|r| |m|e|s|s|a|g|e| 192 | 193 | $ LD_PRELOAD=src/setstdin.so test/setstdin_fread 194 | D|ef|aul|t se|tstdi|n valu|e. Plea|se set P|REENY_STD|IN or PREE|NY_STDIN_FI|LENAME envir|onment variab|les to set you|r own value 195 | 196 | ``` 197 | -------------------------------------------------------------------------------- /cmake-build-multiarch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p build_x64 build_x86 3 | CFLAGS=-m32 CXXFLAGS=-m32 cmake . -Bbuild_x86 4 | (cd build_x86 && make) 5 | cmake . -Bbuild_x64 6 | (cd build_x64 && make) 7 | -------------------------------------------------------------------------------- /cmake-build.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | mkdir -p build 3 | cmake . -Bbuild 4 | (cd build && make) 5 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all dist clean 2 | 3 | # define defaults for architecture specific variables 4 | # the target can either be specified by ARCH=i386 for example or by explictly setting a full target 5 | # triple for example MACHINE=arm-linux-gnueabi 6 | # 7 | # the remaining part of the makefile should only care about MACHINE 8 | CCMACHINE:=$(shell $(CC) -dumpmachine) 9 | CCARCH=$(firstword $(subst -, ,$(MACHINE))) 10 | ARCH:=$(firstword $(subst -, ,$(CCMACHINE))) 11 | # replace ARCH in the target triple (ARCH can be overriden) but only if MACHINE wasn't specified manually 12 | MACHINE?=$(subst $(firstword $(subst -, ,$(CCMACHINE)))-,$(ARCH)-,$(CCMACHINE)) 13 | ARCH:=$(firstword $(subst -, ,$(MACHINE))) 14 | 15 | # if the only difference between CCMACHINE and MACHINE is 32 vs 64 bit, use -m32 16 | ifeq ($(ARCH), $(filter $(ARCH), i386 i486 i586 i686 i786 i886 i986)) 17 | ifeq ($(CCMACHINE), $(subst $(ARCH),x86_64,$(MACHINE))) 18 | ARCHFLAGS:=-m32 19 | endif 20 | else 21 | ifeq ($(ARCH), ppc) 22 | ifeq ($(CCMACHINE), $(subst $(ARCH),ppc64,$(MACHINE))) 23 | ARCHFLAGS:=-m32 24 | endif 25 | endif 26 | endif 27 | 28 | # set default value for CC if target is not standard target for compiler and we don't have flags to set it 29 | ifneq ($(MACHINE),$(CCMACHINE)) 30 | ifeq (,$(ARCHFLAGS)) 31 | CC:=$(MACHINE)-gcc 32 | else 33 | CC:=$(CC) $(ARCHFLAGS) 34 | endif 35 | endif 36 | 37 | 38 | CFLAGS:=$(CFLAGS) -Wall 39 | 40 | ifneq (,$(findstring linux,$(MACHINE))) 41 | CFLAGS:=$(CFLAGS) -DLINUX -ldl 42 | else 43 | ifneq (,$(findstring freebsd,$(MACHINE))) 44 | CFLAGS:=$(CFLAGS) -Dfreebsd 45 | else 46 | ifneq (,$(findstring -elf,$(MACHINE))) 47 | CFLAGS:=$(CFLAGS) -mabicalls 48 | endif 49 | endif 50 | endif 51 | 52 | #$(info $(MACHINE) $(CC)) 53 | 54 | ALL_MODULES=$(filter-out logging.c, $(patsubst %.c,%.so,$(wildcard *.c))) 55 | 56 | all: $(ALL_MODULES) 57 | 58 | COMMON_DEPS=logging.c 59 | desock.so: CFLAGS+=-lpthread 60 | desock_dup.so: CFLAGS+=-ldl 61 | desrand.so: CFLAGS+=-ldl 62 | deptrace.so: CFLAGS+=-ldl 63 | deexec.so: CFLAGS+=-lseccomp 64 | mallocwatch.so: CFLAGS+=-ldl 65 | crazyrealloc.so: CFLAGS+=-ldl 66 | patch.so: CFLAGS+=-ldl -lini_config 67 | eofkiller.so: CFLAGS+=-ldl 68 | setstdin.so: CFLAGS+=-ldl 69 | nowrite.so: CFLAGS+=-ldl 70 | depselect.so: CFLAGS+=-ldl 71 | 72 | %.so: %.c $(COMMON_DEPS) 73 | $(CC) $^ -o $@ -shared -fPIC $(CFLAGS) 74 | 75 | dist: all 76 | mkdir -p ../$(MACHINE) 77 | cp *.so ../$(MACHINE) 78 | 79 | clean: 80 | rm -f *.o 81 | rm -f *.so 82 | 83 | archinfo: 84 | @echo "Compiler architecture: $(CCARCH)" 85 | @echo "Target architecture: $(ARCH)" 86 | @echo "Compiler target triple: $(CCMACHINE)" 87 | @echo "Computed target triple: $(MACHINE)" 88 | @echo "Compiler command: $(CC) $(CFLAGS)" 89 | -------------------------------------------------------------------------------- /src/crazyrealloc.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include "logging.h" 6 | #include 7 | 8 | // 9 | // originals 10 | // 11 | void *(*original_malloc)(size_t); 12 | void (*original_free)(void *); 13 | void *(*original_realloc)(void *, size_t); 14 | void *(*original_memcpy)(void *, const void*, size_t); 15 | __attribute__((constructor)) void preeny_desock_dup_orig() 16 | { 17 | original_malloc = dlsym(RTLD_NEXT, "malloc"); 18 | original_free = dlsym(RTLD_NEXT, "free"); 19 | original_realloc = dlsym(RTLD_NEXT, "realloc"); 20 | original_memcpy = dlsym(RTLD_NEXT, "memcpy"); 21 | } 22 | 23 | 24 | /* 25 | The goal of this function is to catch bugs where people 26 | assume realloc woudn't move stuff around e.g. 27 | 28 | ptr = malloc(...) 29 | realloc(ptr,...) <- ignore return value 30 | <- could be double free, use-after-free, etc 31 | 32 | This is attempted by always assuring that realloc returns 33 | a new pointer to a new memory area regardless of if the old 34 | chunk needed to be moved or not. 35 | */ 36 | 37 | void *realloc(void *ptr, size_t size) 38 | { 39 | void *r = original_realloc(ptr, size); 40 | preeny_info("realloc(%p, %d) == %p\n", ptr, size, r); 41 | if (r != ptr) { 42 | preeny_info("return %p\n", r); 43 | return r; 44 | } else { 45 | if (r) { 46 | void *rm = malloc(size); 47 | preeny_info("malloc(%u) == %p\n", size, rm); 48 | 49 | memcpy(rm, r, size); 50 | preeny_info("memcpy(%p, %p, %u)\n", rm, r, size); 51 | 52 | original_free(r); 53 | preeny_info("free(%p)\n", r); 54 | 55 | preeny_info("return %p\n", rm); 56 | return rm; 57 | 58 | } else { 59 | preeny_info("return %p\n", r); 60 | return r; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/dealarm.c: -------------------------------------------------------------------------------- 1 | #include "logging.h" 2 | 3 | unsigned int alarm(unsigned int seconds) 4 | { 5 | preeny_info("alarm blocked\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/deexec.c: -------------------------------------------------------------------------------- 1 | // compile: gcc -o deexec.so -fPIC -shared deexec.c -lseccomp 2 | // run: LD_PRELOAD=./deexec.so ./prog 3 | #define _GNU_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // save the original __libc_start_main 12 | int (*original__libc_start_main)( 13 | int (*main)(int, char **, char **), int argc, char **argv, 14 | int (*init)(int, char **, char **), void (*fini)(void), 15 | void (*ldso_fini)(void)); 16 | 17 | __attribute__((constructor)) void preeny_deexec_dup_orig() 18 | { 19 | original__libc_start_main = dlsym(RTLD_NEXT, "__libc_start_main"); 20 | } 21 | 22 | void install_seccomp() 23 | { 24 | // Init the filter 25 | scmp_filter_ctx ctx; 26 | ctx = seccomp_init(SCMP_ACT_ALLOW); // default action: allow 27 | 28 | // setup blacklist 29 | seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0); 30 | seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0); 31 | 32 | // build and load the filter 33 | seccomp_load(ctx); 34 | } 35 | 36 | int __libc_start_main( 37 | int (*main)(int, char **, char **), int argc, char **argv, 38 | int (*init)(int, char **, char **), void (*fini)(void), 39 | void (*ldso_fini)(void)) 40 | { 41 | install_seccomp(); 42 | return original__libc_start_main(main, argc, argv, init, fini, ldso_fini); 43 | } 44 | 45 | //int main() 46 | //{ 47 | // install_seccomp(); 48 | //} 49 | -------------------------------------------------------------------------------- /src/defork.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "logging.h" 3 | 4 | pid_t fork(void) { 5 | preeny_info("fork blocked\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/depselect.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "logging.h" 11 | 12 | int (*real_pselect)(int, fd_set*, fd_set*, fd_set*, const struct timespec*, const sigset_t*); 13 | int (*real_pselect6)(int, fd_set*, fd_set*, fd_set*, const struct timespec*, const sigset_t*); 14 | int (*real_select)(int, fd_set*, fd_set*, fd_set*, struct timeval*); 15 | char* returned_fd_env; 16 | int returned_fd; 17 | 18 | __attribute__((constructor)) void __depselect_init(void) 19 | { 20 | real_pselect = dlsym(RTLD_NEXT, "pselect"); 21 | real_pselect6 = dlsym(RTLD_NEXT, "pselect6"); 22 | real_select = dlsym(RTLD_NEXT, "select"); 23 | 24 | returned_fd_env = getenv("PREENY_DEPSELECT_RETURNED_FD"); 25 | 26 | if (returned_fd_env == NULL) 27 | { 28 | returned_fd = 1; 29 | } 30 | else 31 | { 32 | returned_fd = atoi(returned_fd_env); 33 | } 34 | } 35 | 36 | int pselect(int nfds, fd_set* restrict readfds, fd_set* restrict writefds, fd_set* restrict exceptfds, const struct timespec* restrict timeout, const __sigset_t* restrict sigmask) 37 | { 38 | preeny_info("pselect() blocked, returned fd : %d\n", returned_fd); 39 | 40 | return returned_fd; 41 | } 42 | 43 | int pselect6(int nfds, fd_set* restrict readfds, fd_set* restrict writefds, fd_set* restrict exceptfds, const struct timespec* restrict timeout, const __sigset_t* restrict sigmask) 44 | { 45 | preeny_info("pselect6() blocked, returned fd : %d\n", returned_fd); 46 | 47 | return returned_fd; 48 | } 49 | 50 | int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout) 51 | { 52 | preeny_info("select() blocked, returned fd : %d\n", returned_fd); 53 | 54 | return returned_fd; 55 | } -------------------------------------------------------------------------------- /src/deptrace.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 1 2 | #include 3 | #include "logging.h" 4 | 5 | long ptrace(int a, int b, int c, int d) { 6 | preeny_info("ptrace blocked!\n"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/derand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "logging.h" 3 | 4 | int rand(void) 5 | { 6 | char *r_str = getenv("RAND"); 7 | int r; 8 | 9 | if (r_str) r = atoi(r_str); 10 | else r = 42; 11 | 12 | preeny_debug("random changed to %d\n", r); 13 | return r; 14 | } 15 | 16 | int rand_r(unsigned int *a) 17 | { 18 | return rand(); 19 | } 20 | 21 | long random() 22 | { 23 | return rand(); 24 | } 25 | -------------------------------------------------------------------------------- /src/desigact.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "logging.h" 3 | 4 | int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) 5 | { 6 | preeny_info("sigaction on signal %d blocked\n", signum); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/desleep.c: -------------------------------------------------------------------------------- 1 | #include "logging.h" 2 | #include 3 | 4 | unsigned int sleep(unsigned int seconds) 5 | { 6 | preeny_debug("Just spared you %u seconds!\n", seconds); 7 | return 0; 8 | } 9 | 10 | unsigned int usleep(unsigned int microseconds) 11 | { 12 | preeny_debug("Just spared you %u microseconds!\n", microseconds); 13 | return 0; 14 | } 15 | 16 | int nanosleep(const struct timespec *req, struct timespec *rem) { 17 | preeny_debug("Just spared you %u seconds and %u nanoseconds!\n", req->tv_sec, req->tv_nsec); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /src/desock.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "logging.h" 17 | 18 | #define PREENY_MAX_FD 8192 19 | #define PREENY_SOCKET_OFFSET 500 20 | #define READ_BUF_SIZE 65536 21 | 22 | #define PREENY_SIN_PORT 9000 23 | 24 | #define PREENY_SOCKET(x) (x+PREENY_SOCKET_OFFSET) 25 | 26 | int preeny_desock_shutdown_flag = 0; 27 | int preeny_desock_accepted_sock = -1; 28 | 29 | pthread_t *preeny_socket_threads_to_front[PREENY_MAX_FD] = { 0 }; 30 | pthread_t *preeny_socket_threads_to_back[PREENY_MAX_FD] = { 0 }; 31 | 32 | int preeny_socket_sync(int from, int to, int timeout) 33 | { 34 | struct pollfd poll_in = { from, POLLIN, 0 }; 35 | char read_buf[READ_BUF_SIZE]; 36 | int total_n; 37 | char error_buf[1024]; 38 | int n; 39 | int r; 40 | 41 | r = poll(&poll_in, 1, timeout); 42 | if (r < 0) 43 | { 44 | strerror_r(errno, error_buf, 1024); 45 | preeny_debug("read poll() received error '%s' on fd %d\n", error_buf, from); 46 | return 0; 47 | } 48 | else if (poll_in.revents == 0) 49 | { 50 | preeny_debug("read poll() timed out on fd %d\n", from); 51 | return 0; 52 | } 53 | 54 | total_n = read(from, read_buf, READ_BUF_SIZE); 55 | if (total_n < 0) 56 | { 57 | strerror_r(errno, error_buf, 1024); 58 | preeny_info("synchronization of fd %d to %d shutting down due to read error '%s'\n", from, to, error_buf); 59 | return -1; 60 | } 61 | else if (total_n == 0 && from == 0) 62 | { 63 | preeny_info("synchronization of fd %d to %d shutting down due to EOF\n", from, to); 64 | return -1; 65 | } 66 | preeny_debug("read %d bytes from %d (will write to %d)\n", total_n, from, to); 67 | 68 | n = 0; 69 | while (n != total_n) 70 | { 71 | r = write(to, read_buf, total_n - n); 72 | if (r < 0) 73 | { 74 | strerror_r(errno, error_buf, 1024); 75 | preeny_info("synchronization of fd %d to %d shutting down due to read error '%s'\n", from, to, error_buf); 76 | return -1; 77 | } 78 | n += r; 79 | } 80 | 81 | preeny_debug("wrote %d bytes to %d (had read from %d)\n", total_n, to, from); 82 | return total_n; 83 | } 84 | 85 | __attribute__((destructor)) void preeny_desock_shutdown() 86 | { 87 | int i; 88 | int to_sync[PREENY_MAX_FD] = { }; 89 | 90 | preeny_debug("shutting down desock...\n"); 91 | preeny_desock_shutdown_flag = 1; 92 | 93 | 94 | for (i = 0; i < PREENY_MAX_FD; i++) 95 | { 96 | if (preeny_socket_threads_to_front[i]) 97 | { 98 | preeny_debug("sending SIGINT to thread %d...\n", i); 99 | pthread_join(*preeny_socket_threads_to_front[i], NULL); 100 | pthread_join(*preeny_socket_threads_to_back[i], NULL); 101 | preeny_debug("... sent!\n"); 102 | to_sync[i] = 1; 103 | } 104 | } 105 | 106 | for (i = 0; i < PREENY_MAX_FD; i++) 107 | { 108 | if (to_sync[i]) 109 | { 110 | //while (preeny_socket_sync(0, PREENY_SOCKET(i), 10) > 0); 111 | while (preeny_socket_sync(PREENY_SOCKET(i), 1, 0) > 0); 112 | } 113 | } 114 | 115 | preeny_debug("... shutdown complete!\n"); 116 | } 117 | 118 | void preeny_socket_sync_loop(int from, int to) 119 | { 120 | int r; 121 | 122 | preeny_debug("starting forwarding from %d to %d!\n", from, to); 123 | 124 | while (!preeny_desock_shutdown_flag) 125 | { 126 | r = preeny_socket_sync(from, to, 15); 127 | if (r < 0) return; 128 | } 129 | } 130 | 131 | #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" 132 | #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" 133 | 134 | void *preeny_socket_sync_to_back(void *fd) 135 | { 136 | int front_fd = (int)fd; 137 | int back_fd = PREENY_SOCKET(front_fd); 138 | preeny_socket_sync_loop(back_fd, 1); 139 | return NULL; 140 | } 141 | 142 | void *preeny_socket_sync_to_front(void *fd) 143 | { 144 | int front_fd = (int)fd; 145 | int back_fd = PREENY_SOCKET(front_fd); 146 | preeny_socket_sync_loop(0, back_fd); 147 | shutdown(back_fd, SHUT_WR); 148 | return NULL; 149 | } 150 | 151 | // 152 | // originals 153 | // 154 | int (*original_socket)(int, int, int); 155 | int (*original_bind)(int, const struct sockaddr *, socklen_t); 156 | int (*original_listen)(int, int); 157 | int (*original_accept)(int, struct sockaddr *, socklen_t *); 158 | int (*original_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 159 | int (*original_close)(int fd); 160 | int (*original_shutdown)(int sockfd, int how); 161 | int (*original_getsockname)(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 162 | __attribute__((constructor)) void preeny_desock_orig() 163 | { 164 | original_socket = dlsym(RTLD_NEXT, "socket"); 165 | original_listen = dlsym(RTLD_NEXT, "listen"); 166 | original_accept = dlsym(RTLD_NEXT, "accept"); 167 | original_bind = dlsym(RTLD_NEXT, "bind"); 168 | original_connect = dlsym(RTLD_NEXT, "connect"); 169 | original_close = dlsym(RTLD_NEXT, "close"); 170 | original_shutdown = dlsym(RTLD_NEXT, "shutdown"); 171 | original_getsockname = dlsym(RTLD_NEXT, "getsockname"); 172 | } 173 | 174 | int socket(int domain, int type, int protocol) 175 | { 176 | int fds[2]; 177 | int front_socket; 178 | int back_socket; 179 | 180 | if (domain != AF_INET && domain != AF_INET6) 181 | { 182 | preeny_info("Ignoring non-internet socket."); 183 | return original_socket(domain, type, protocol); 184 | } 185 | 186 | int r = socketpair(AF_UNIX, type, 0, fds); 187 | preeny_debug("Intercepted socket()!\n"); 188 | 189 | if (r != 0) 190 | { 191 | perror("preeny socket emulation failed:"); 192 | return -1; 193 | } 194 | 195 | preeny_debug("... created socket pair (%d, %d)\n", fds[0], fds[1]); 196 | 197 | front_socket = fds[0]; 198 | back_socket = dup2(fds[1], PREENY_SOCKET(front_socket)); 199 | close(fds[1]); 200 | 201 | preeny_debug("... dup into socketpair (%d, %d)\n", fds[0], back_socket); 202 | 203 | preeny_socket_threads_to_front[fds[0]] = malloc(sizeof(pthread_t)); 204 | preeny_socket_threads_to_back[fds[0]] = malloc(sizeof(pthread_t)); 205 | 206 | r = pthread_create(preeny_socket_threads_to_front[fds[0]], NULL, (void*(*)(void*))preeny_socket_sync_to_front, (void *)front_socket); 207 | if (r) 208 | { 209 | perror("failed creating front-sync thread"); 210 | return -1; 211 | } 212 | 213 | r = pthread_create(preeny_socket_threads_to_back[fds[0]], NULL, (void*(*)(void*))preeny_socket_sync_to_back, (void *)front_socket); 214 | if (r) 215 | { 216 | perror("failed creating back-sync thread"); 217 | return -1; 218 | } 219 | 220 | return fds[0]; 221 | } 222 | 223 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 224 | { 225 | if (preeny_desock_accepted_sock >= 0) 226 | { 227 | errno = ECONNRESET; 228 | return -1; 229 | } 230 | 231 | //initialize a sockaddr_in for the peer 232 | struct sockaddr_in peer_addr; 233 | memset(&peer_addr, '0', sizeof(struct sockaddr_in)); 234 | 235 | //Set the contents in the peer's sock_addr. 236 | //Make sure the contents will simulate a real client that connects with the intercepted server, as the server may depend on the contents to make further decisions. 237 | //The followings set-up should be fine with Nginx. 238 | peer_addr.sin_family = AF_INET; 239 | peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); 240 | peer_addr.sin_port = htons(PREENY_SIN_PORT); 241 | 242 | //copy the initialized peer_addr back to the original sockaddr. Note the space for the original sockaddr, namely addr, has already been allocated 243 | if (addr) memcpy(addr, &peer_addr, sizeof(struct sockaddr_in)); 244 | 245 | if (preeny_socket_threads_to_front[sockfd]) 246 | { 247 | preeny_desock_accepted_sock = dup(sockfd); 248 | return preeny_desock_accepted_sock; 249 | } 250 | else return original_accept(sockfd, addr, addrlen); 251 | } 252 | 253 | int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) 254 | { 255 | return accept(sockfd, addr, addrlen); 256 | } 257 | 258 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 259 | { 260 | if (preeny_socket_threads_to_front[sockfd]) 261 | { 262 | preeny_info("Emulating bind on port %d\n", ntohs(((struct sockaddr_in*)addr)->sin_port)); 263 | return 0; 264 | } 265 | else 266 | { 267 | return original_bind(sockfd, addr, addrlen); 268 | } 269 | } 270 | 271 | int listen(int sockfd, int backlog) 272 | { 273 | if (preeny_socket_threads_to_front[sockfd]) return 0; 274 | else return original_listen(sockfd, backlog); 275 | } 276 | 277 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 278 | { 279 | if (preeny_socket_threads_to_front[sockfd]) return 0; 280 | else return original_connect(sockfd, addr, addrlen); 281 | } 282 | 283 | int close(int fd) { 284 | if (preeny_desock_accepted_sock != -1 && preeny_desock_accepted_sock == fd) 285 | exit(0); 286 | 287 | return original_close(fd); 288 | } 289 | 290 | int shutdown(int sockfd, int how) { 291 | if (preeny_desock_accepted_sock != -1 && preeny_desock_accepted_sock == sockfd) 292 | exit(0); 293 | 294 | return original_shutdown(sockfd, how); 295 | } 296 | 297 | int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { 298 | struct sockaddr_in target; 299 | socklen_t copylen = sizeof(target); 300 | 301 | if (!preeny_socket_threads_to_front[sockfd]) 302 | return original_getsockname(sockfd, addr, addrlen); 303 | 304 | if (!addr || !addrlen) 305 | return -1; 306 | 307 | if (*addrlen < sizeof(target)) 308 | copylen = *addrlen; 309 | 310 | target.sin_family = AF_INET; 311 | target.sin_addr.s_addr = htonl(INADDR_ANY); 312 | target.sin_port = htons(PREENY_SIN_PORT); 313 | 314 | memcpy(addr, &target, copylen); 315 | *addrlen = copylen; 316 | 317 | return 0; 318 | } 319 | -------------------------------------------------------------------------------- /src/desock_dup.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "logging.h" 16 | 17 | // 18 | // originals 19 | // 20 | int (*original_close)(int); 21 | int (*original_dup2)(int, int); 22 | __attribute__((constructor)) void preeny_desock_dup_orig() 23 | { 24 | original_close = dlsym(RTLD_NEXT, "close"); 25 | original_dup2 = dlsym(RTLD_NEXT, "dup2"); 26 | } 27 | 28 | int close(int sockfd) 29 | { 30 | if (sockfd <= 2) 31 | { 32 | preeny_info("Disabling close on %d\n", sockfd); 33 | return 0; 34 | } 35 | else 36 | { 37 | return original_close(sockfd); 38 | } 39 | } 40 | 41 | int dup2(int old, int new) 42 | { 43 | if (new <= 2) 44 | { 45 | preeny_info("Disabling dup from %d to %d\n", old, new); 46 | return 0; 47 | } 48 | else 49 | { 50 | return original_dup2(old, new); 51 | } 52 | } 53 | 54 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 55 | { 56 | preeny_info("Emulating accept on %d\n", sockfd); 57 | return 0; 58 | } 59 | 60 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 61 | { 62 | preeny_info("Emulating bind on port %d\n", ntohs(((struct sockaddr_in*)addr)->sin_port)); 63 | return 0; 64 | } 65 | 66 | int listen(int sockfd, int backlog) 67 | { 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /src/desrand.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include "logging.h" 6 | 7 | void srand(unsigned int s) 8 | { 9 | char *r_str = getenv("WANT"); 10 | char *skip_str = getenv("SKIP"); 11 | char *mod_str = getenv("MOD"); 12 | char *seed_str = getenv("SEED"); 13 | int seed; 14 | int skip; 15 | int mod; 16 | int r; 17 | int i; 18 | 19 | void (*original_srand)(unsigned int); 20 | original_srand = dlsym(RTLD_NEXT, "srand"); 21 | 22 | if (seed_str) seed = atoi(seed_str); 23 | else if (!r_str || !mod_str) seed = 42; 24 | else 25 | { 26 | if (!skip_str) skip = 0; 27 | else skip = atoi(skip_str); 28 | 29 | mod = atoi(mod_str); 30 | r = atoi(r_str); 31 | 32 | seed = 0; 33 | while (1) 34 | { 35 | original_srand(seed); 36 | preeny_debug("trying seed %d\n", seed); 37 | for (i = 0; i < skip; i++) rand(); 38 | preeny_debug("... skipped %d values\n", i); 39 | i = rand(); 40 | preeny_debug("... got: %d %% %d == %d\n", i, mod, i%mod); 41 | if (i%mod == r) break; 42 | seed++; 43 | } 44 | 45 | preeny_info("brute-forced seed of %d\n", seed); 46 | } 47 | 48 | preeny_debug("setting seed of %d\n", seed); 49 | original_srand(seed); 50 | return; 51 | } 52 | -------------------------------------------------------------------------------- /src/detime.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #ifdef __unix__ 6 | #include 7 | #include 8 | #endif 9 | 10 | #include "logging.h" 11 | 12 | time_t time(time_t *res) 13 | { 14 | char *time_str = getenv("TIME"); 15 | time_t time = time_str ? atoi(time_str) : 0; 16 | if(res != NULL) { 17 | *res = time; 18 | } 19 | 20 | preeny_debug("time frozen at %d\n", time); 21 | return time; 22 | } 23 | 24 | #ifdef __unix__ 25 | #if !defined(__GLIBC__) || __GLIBC_PREREQ(2, 31) 26 | int gettimeofday(struct timeval *tv, void *__restrict tz) 27 | #else 28 | int gettimeofday(struct timeval *tv, struct timezone *tz) 29 | #endif 30 | { 31 | 32 | char *sec_str = getenv("TV_SEC"); 33 | char *usec_str = getenv("TV_USEC"); 34 | tv->tv_sec = sec_str ? atoi(sec_str) : 0; 35 | tv->tv_usec = usec_str ? atoi(usec_str) : 0; 36 | 37 | preeny_debug("gettimeofday frozen at %ld.%d\n", tv->tv_sec, tv->tv_usec); 38 | 39 | 40 | #if !defined(__GLIBC__) || __GLIBC_PREREQ(2, 31) 41 | #else 42 | if (tz != NULL) { 43 | char *minwest_str = getenv("TZ_MINWEST"); 44 | char *dst_str = getenv("TZ_DSTTIME"); 45 | tz->tz_minuteswest = minwest_str ? atoi(minwest_str) : 0; 46 | tz->tz_dsttime = dst_str ? atoi(dst_str) : 0; 47 | 48 | preeny_debug("gettimeofday tz frozen at -%d minm, DST: %d\n", tz->tz_minuteswest, tz->tz_dsttime); 49 | } 50 | #endif 51 | 52 | return 0; 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /src/deuid.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include "logging.h" 5 | 6 | uid_t getuid(void) 7 | { 8 | char *uid_str = getenv("FAKE_UID"); 9 | uid_t uid = uid_str ? atoi(uid_str) : 0; 10 | 11 | preeny_debug("User ID set to %d\n", uid); 12 | return uid; 13 | } 14 | 15 | uid_t geteuid(void) 16 | { 17 | char *euid_str = getenv("FAKE_EUID"); 18 | uid_t euid = euid_str ? atoi(euid_str) : 0; 19 | 20 | preeny_debug("User ID set to %d\n", euid); 21 | return euid; 22 | } 23 | -------------------------------------------------------------------------------- /src/ensock.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "logging.h" 17 | 18 | // 19 | // originals 20 | // 21 | __attribute__((constructor)) void preeny_socketize() 22 | { 23 | char *port_str = getenv("PORT"); 24 | int port = port_str ? atoi(port_str) : 1337; 25 | int fd = socket(AF_INET, SOCK_STREAM, 0); 26 | int conn_fd; 27 | struct sockaddr_in serv_addr, client_addr; 28 | unsigned int client_len = sizeof(client_addr); 29 | 30 | if (fd < 0) 31 | { 32 | preeny_error("Socket error\n"); 33 | exit(1); 34 | } 35 | 36 | int optval = 1; 37 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 38 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & (~O_NONBLOCK)); 39 | 40 | bzero(&serv_addr, sizeof(serv_addr)); 41 | serv_addr.sin_family = AF_INET; 42 | serv_addr.sin_addr.s_addr = INADDR_ANY; 43 | serv_addr.sin_port = htons(port); 44 | if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 45 | { 46 | preeny_error("Unable to bind\n"); 47 | exit(1); 48 | } 49 | 50 | listen(fd, 0); 51 | puts("LISTENED"); 52 | conn_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len); 53 | fcntl(conn_fd, F_SETFL, fcntl(conn_fd, F_GETFL, 0) & (~O_NONBLOCK)); 54 | puts("ACCEPTED"); 55 | if (conn_fd < 0) 56 | { 57 | preeny_error("Accept fail\n"); 58 | exit(1); 59 | } 60 | close(fd); 61 | puts("CLOSED"); 62 | 63 | dup2(conn_fd, 0); 64 | dup2(conn_fd, 1); 65 | dup2(conn_fd, 2); 66 | puts("DUPED"); 67 | } 68 | -------------------------------------------------------------------------------- /src/eofkiller.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "logging.h" 9 | 10 | #define HOOK_FN(ret, name, args...) typedef ret (* name##_t)(args); \ 11 | ret name(args) { \ 12 | static name##_t o_##name; \ 13 | if (!o_##name) \ 14 | o_##name = (name##_t) dlsym(RTLD_NEXT, #name); \ 15 | ret result = o_##name 16 | #define HOOK_END(cond) if (result cond && !isatty(fileno(stdin))) { \ 17 | preeny_info("EOF, exiting."); \ 18 | exit(0); \ 19 | } else return result; } 20 | 21 | int hook_fd; 22 | 23 | HOOK_FN(char*, fgets, char *s, int size, FILE *stream)(s, size, stream); 24 | HOOK_END(== NULL && fileno(stream) == hook_fd); 25 | 26 | HOOK_FN(int, fgetc, FILE *stream)(stream); 27 | HOOK_END(== EOF && fileno(stream) == hook_fd); 28 | 29 | HOOK_FN(int, getc, FILE *stream)(stream); 30 | HOOK_END(== EOF && fileno(stream) == hook_fd); 31 | 32 | HOOK_FN(int, getchar, void)(); 33 | HOOK_END(== EOF); 34 | 35 | HOOK_FN(char*, gets, char *s)(s); 36 | HOOK_END(== NULL); 37 | 38 | HOOK_FN(ssize_t, read, int fd, void *buf, size_t count)(fd, buf, count); 39 | HOOK_END(<= 0 && fd == hook_fd); 40 | 41 | char scanf_eof_on_malformed; 42 | int handle_scanf_result(int result) { 43 | if (result == EOF) { 44 | preeny_info("EOF, exiting."); 45 | exit(0); 46 | } 47 | if (scanf_eof_on_malformed && !result) { 48 | preeny_info("Malformed scanf input, exiting."); 49 | exit(0); 50 | } 51 | return result; 52 | } 53 | 54 | typedef int (*vscanf_t)(const char *format, va_list ap); 55 | vscanf_t o_vscanf; 56 | int scanf(const char *format, ...) { 57 | va_list args; 58 | va_start(args, format); 59 | int result = o_vscanf(format, args); 60 | va_end(args); 61 | return handle_scanf_result(result); 62 | } 63 | 64 | #ifdef __isoc99_scanf 65 | int __isoc99_scanf(const char *format, ...) { 66 | va_list args; 67 | va_start(args, format); 68 | int result = o_vscanf(format, args); 69 | va_end(args); 70 | return handle_scanf_result(result); 71 | } 72 | #endif 73 | 74 | typedef int (*vfscanf_t)(FILE *stream, const char *format, va_list ap); 75 | vfscanf_t o_vfscanf; 76 | int fscanf(FILE *stream, const char *format, ...) { 77 | va_list args; 78 | va_start(args, format); 79 | int result = o_vfscanf(stream, format, args); 80 | va_end(args); 81 | if (fileno(stream) == hook_fd) 82 | return handle_scanf_result(result); 83 | else 84 | return result; 85 | } 86 | 87 | #ifdef __isoc99_fscanf 88 | int __isoc99_fscanf(FILE *stream, const char *format, ...) { 89 | va_list args; 90 | va_start(args, format); 91 | int result = o_vfscanf(stream, format, args); 92 | va_end(args); 93 | if (fileno(stream) == hook_fd) 94 | return handle_scanf_result(result); 95 | else 96 | return result; 97 | } 98 | #endif 99 | 100 | __attribute__((constructor)) 101 | int main() { 102 | scanf_eof_on_malformed = getenv("SCANF_EOF_ON_MALFORMED") != NULL; 103 | 104 | char *fd_str = getenv("EOF_HOOK_FD"); 105 | hook_fd = fd_str ? atoi(fd_str) : fileno(stdin); 106 | 107 | o_vscanf = dlsym(RTLD_NEXT, "vscanf"); 108 | o_vfscanf = dlsym(RTLD_NEXT, "vfscanf"); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/getcanary.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | 5 | #include "logging.h" 6 | 7 | /** 8 | * Copyright (c) 2017 elttam 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #ifdef __x86_64__ 30 | #define canary_t uint64_t 31 | #define INSN_READ "movq %%fs:0x28, %0;" 32 | #define FMT "Found canary: %#lx\n" 33 | 34 | #elif __i386__ 35 | #define canary_t uint32_t 36 | #define INSN_READ "movl %%gs:0x14, %0;" 37 | #define FMT "Found canary: %#x\n" 38 | 39 | #elif __arm__ 40 | #define canary_t uint32_t 41 | #define INSN_READ "ldr r0, =__stack_chk_guard; ldr r0, [r0]; mov %0, r0;" 42 | #define FMT "Found canary: %#x\n" 43 | #endif 44 | 45 | canary_t read_canary() 46 | { 47 | canary_t val = 0; 48 | 49 | __asm__(INSN_READ 50 | : "=r"(val) 51 | : 52 | :); 53 | 54 | return val; 55 | } 56 | 57 | void preeny_logging_init(); 58 | 59 | // 60 | // Dump stack cookie on startup 61 | // 62 | __attribute__((constructor)) void preeny_cookie_dump() 63 | { 64 | preeny_logging_init(); //This haven't been called when we get here so env flags are not respected unless called manually 65 | preeny_info(FMT, read_canary()); 66 | } 67 | -------------------------------------------------------------------------------- /src/logging.c: -------------------------------------------------------------------------------- 1 | // This code is licenced under BSD (see LICENSE file) by Yan Shoshitaishvili 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int preeny_debug_on = 0; 9 | int preeny_info_on = 0; 10 | int preeny_error_on = 1; 11 | 12 | void preeny_debug(char *fmt, ...) 13 | { 14 | if (!preeny_debug_on) return; 15 | 16 | printf("+++ "); 17 | va_list args; 18 | va_start(args,fmt); 19 | vprintf(fmt,args); 20 | va_end(args); 21 | 22 | fflush(stdout); 23 | } 24 | 25 | void preeny_info(char *fmt, ...) 26 | { 27 | if (!preeny_info_on) return; 28 | 29 | printf("--- "); 30 | va_list args; 31 | va_start(args,fmt); 32 | vprintf(fmt,args); 33 | va_end(args); 34 | 35 | fflush(stdout); 36 | } 37 | 38 | void preeny_error(char *fmt, ...) 39 | { 40 | if (!preeny_error_on) return; 41 | 42 | fprintf(stderr, "!!! ERROR: "); 43 | va_list args; 44 | va_start(args,fmt); 45 | vfprintf(stderr, fmt,args); 46 | va_end(args); 47 | 48 | fflush(stderr); 49 | } 50 | 51 | __attribute__((constructor)) void preeny_logging_init() 52 | { 53 | preeny_debug_on = preeny_debug_on || (getenv("PREENY_DEBUG") && (strcmp(getenv("PREENY_DEBUG"), "1") == 0)); 54 | preeny_info_on = preeny_info_on || (getenv("PREENY_INFO") && (strcmp(getenv("PREENY_INFO"), "1") == 0)); 55 | preeny_error_on = preeny_error_on || (getenv("PREENY_ERROR") && (strcmp(getenv("PREENY_ERROR"), "1") == 0)); 56 | } 57 | -------------------------------------------------------------------------------- /src/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern int preeny_debug_on; 4 | extern int preeny_info_on; 5 | extern int preeny_error_on; 6 | 7 | void preeny_debug(char *, ...); 8 | void preeny_info(char *, ...); 9 | void preeny_error(char *, ...); 10 | -------------------------------------------------------------------------------- /src/mallocwatch.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include "logging.h" 6 | 7 | 8 | int malloc_hook_active = 1; 9 | int free_hook_active = 1; 10 | int calloc_hook_active = 1; 11 | int realloc_hook_active = 1; 12 | 13 | // 14 | // originals 15 | // 16 | void *(*original_malloc)(size_t); 17 | void (*original_free)(void *); 18 | void *(*original_calloc)(size_t, size_t); 19 | void *(*original_realloc)(void *, size_t); 20 | __attribute__((constructor)) void preeny_mallocwatch_orig() 21 | { 22 | original_malloc = dlsym(RTLD_NEXT, "malloc"); 23 | original_free = dlsym(RTLD_NEXT, "free"); 24 | original_calloc = dlsym(RTLD_NEXT, "calloc"); 25 | original_realloc = dlsym(RTLD_NEXT, "realloc"); 26 | } 27 | 28 | void *malloc(size_t size) 29 | { 30 | if(!original_malloc) 31 | preeny_mallocwatch_orig(); 32 | 33 | void *r = original_malloc(size); 34 | if(malloc_hook_active) { 35 | malloc_hook_active = 0; 36 | preeny_info("malloc(%d) == %p\n", size, r); 37 | malloc_hook_active = 1; 38 | } 39 | return r; 40 | } 41 | 42 | void free(void *ptr) 43 | { 44 | if(!original_free) 45 | preeny_mallocwatch_orig(); 46 | 47 | original_free(ptr); 48 | if(free_hook_active) { 49 | free_hook_active = 0; 50 | preeny_info("free(%p)\n", ptr); 51 | free_hook_active = 1; 52 | } 53 | } 54 | 55 | void *calloc(size_t nmemb, size_t size) 56 | { 57 | if(!original_calloc) 58 | preeny_mallocwatch_orig(); 59 | 60 | void *r = original_calloc(nmemb, size); 61 | if(calloc_hook_active) { 62 | calloc_hook_active = 0; 63 | preeny_info("calloc(%d, %d) == %p\n", nmemb, size, r); 64 | calloc_hook_active = 1; 65 | } 66 | return r; 67 | } 68 | 69 | void *realloc(void *ptr, size_t size) 70 | { 71 | if(!original_realloc) 72 | preeny_mallocwatch_orig(); 73 | 74 | void *r = original_realloc(ptr, size); 75 | if(realloc_hook_active) { 76 | realloc_hook_active = 0; 77 | preeny_info("realloc(%p, %d) == %p\n", ptr, size, r); 78 | realloc_hook_active = 1; 79 | } 80 | return r; 81 | } 82 | -------------------------------------------------------------------------------- /src/nowrite.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int (*original_open)(const char *pathname, int flags, ...); 9 | 10 | __attribute__((constructor)) void preeny_nowrite() 11 | { 12 | original_open = dlsym(RTLD_NEXT, "open"); 13 | } 14 | 15 | int open(const char *pathname, int flags, ...) { 16 | // Strip write-related flags & force readonly flag 17 | flags &= ~(O_WRONLY|O_RDWR|O_CLOEXEC|O_CREAT|O_DIRECTORY|O_EXCL|O_NOCTTY|O_NOFOLLOW|O_TMPFILE|O_TRUNC); 18 | flags |= O_RDONLY; 19 | 20 | return original_open(pathname, flags); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/patch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "logging.h" 8 | 9 | struct collection_item *preeny_patch_load(char *conf_file) 10 | { 11 | struct collection_item *diff_conf_errors = NULL; 12 | struct collection_item *diff_conf = NULL; 13 | int r; 14 | 15 | preeny_debug("loading config file %s\n", conf_file); 16 | 17 | r = config_from_file("preeny_patch", conf_file, &diff_conf, 0, &diff_conf_errors); 18 | if (r != 0) 19 | { 20 | if (errno == 0) 21 | { 22 | preeny_error("patch file %s contains errors\n", conf_file); 23 | if (preeny_error_on) 24 | { 25 | print_file_parsing_errors(stderr, diff_conf_errors); 26 | fprintf(stderr, "\n"); 27 | free_ini_config_errors(diff_conf_errors); 28 | } 29 | } 30 | else perror("loading config file failed"); 31 | return NULL; 32 | } 33 | else 34 | { 35 | preeny_debug("config file %s successfully loaded!\n", conf_file); 36 | } 37 | 38 | return diff_conf; 39 | } 40 | 41 | struct collection_item *preeny_patch_get_config_item(char *conf_file, char *section, struct collection_item *patch, char *name) 42 | { 43 | struct collection_item *item = NULL; 44 | int error; 45 | 46 | error = get_config_item(section, name, patch, &item); 47 | if (!item || error != 0) 48 | { 49 | preeny_debug("couldn't get %s item from section %s in patchfile %s\n", name, section, conf_file); 50 | return NULL; 51 | } 52 | 53 | return item; 54 | } 55 | 56 | void *preeny_patch_get_pointer(char *conf_file, char *section, struct collection_item *patch, char *name) 57 | { 58 | struct collection_item *item = NULL; 59 | int error; 60 | 61 | const char *ptr_str; 62 | void *ptr; 63 | 64 | item = preeny_patch_get_config_item(conf_file, section, patch, name); 65 | if (!item) { preeny_error("error getting %s from section %s in patchfile %s\n", name, section, conf_file); return NULL; } 66 | 67 | ptr_str = get_const_string_config_value(item, &error); 68 | if (error != 0) { preeny_error("error converting %s from section %s in patchfile %s\n", name, section, conf_file); return NULL; } 69 | 70 | sscanf(ptr_str, "%p", &ptr); 71 | preeny_debug("retrieved %s: %p\n", name, ptr); 72 | return ptr; 73 | } 74 | 75 | void *preeny_patch_get_content(char *conf_file, char *section, struct collection_item *patch, int *content_length) 76 | { 77 | struct collection_item *item = NULL; 78 | int error; 79 | 80 | void *content; 81 | 82 | item = preeny_patch_get_config_item(conf_file, section, patch, "content"); 83 | if (!item) return NULL; 84 | 85 | content = get_bin_config_value(item, content_length, &error); 86 | if (error != 0) { preeny_error("error converting content from section %s in patchfile %s\n", section, conf_file); return NULL; } 87 | 88 | return content; 89 | } 90 | 91 | int preeny_patch_apply_patch(void *target, void *content, int length) 92 | { 93 | char error_str[1024]; 94 | int error; 95 | int pagesize = getpagesize(); 96 | uintptr_t target_page = ((uintptr_t)target)/pagesize*pagesize; 97 | 98 | preeny_debug("mprotecting pages containing %d bytes at %p so that we can write the patch\n", length, target); 99 | error = mprotect((void *)target_page, length, PROT_READ | PROT_WRITE | PROT_EXEC); 100 | if (error != 0) 101 | { 102 | strerror_r(errno, error_str, 1024); 103 | preeny_error("error '%s' making pages containing %d bytes at %p writeable\n", error_str, length, target); 104 | return error; 105 | } 106 | 107 | preeny_debug("writing %d bytes at %p\n", length, target); 108 | memcpy(target, content, length); 109 | 110 | preeny_debug("wrote %d bytes at %p\n", length, target); 111 | 112 | return 0; 113 | } 114 | 115 | int preeny_patch_apply_file(char *conf_file, struct collection_item *patch) 116 | { 117 | int num_sections = 0; 118 | char **sections; 119 | char *section; 120 | int error = 0; 121 | int i; 122 | 123 | void *addr; 124 | void *content; 125 | int content_length = 0; 126 | 127 | sections = get_section_list(patch, &num_sections, &error); 128 | if (error > 0) { preeny_error("error getting section list from %s\n", conf_file); return -1; } 129 | 130 | for (i = 0; i < num_sections; i++) 131 | { 132 | section = sections[i]; 133 | preeny_debug("apply patches for section %s in file %s\n", section, conf_file); 134 | 135 | addr = preeny_patch_get_pointer(conf_file, section, patch, "address"); 136 | if (addr == NULL) 137 | { 138 | preeny_error("got NULL target for section %s from %s\n", section, conf_file); 139 | return -1; 140 | } 141 | 142 | content = preeny_patch_get_content(conf_file, section, patch, &content_length); 143 | if (content == NULL) 144 | { 145 | preeny_error("got NULL content for section %s from %s\n", section, conf_file); 146 | return -1; 147 | } 148 | 149 | preeny_info("section %s in file %s specifies %d-byte patch at %p\n", section, conf_file, content_length, addr); 150 | 151 | error = preeny_patch_apply_patch(addr, content, content_length); 152 | free(content); 153 | if (error > 0) { preeny_error("error applying patch section %s from %s\n", section, conf_file); return -1; } 154 | } 155 | 156 | return 0; 157 | } 158 | 159 | __attribute__((constructor)) void preeny_patch_program() 160 | { 161 | char *patchfile = getenv("PATCH"); 162 | struct collection_item *p; 163 | 164 | if (patchfile) 165 | { 166 | p = preeny_patch_load(patchfile); 167 | if (p == NULL) exit(137); 168 | 169 | preeny_patch_apply_file(patchfile, p); 170 | } 171 | 172 | preeny_debug("done patching!\n"); 173 | } 174 | -------------------------------------------------------------------------------- /src/setcanary.c: -------------------------------------------------------------------------------- 1 | #include "logging.h" 2 | #include 3 | #include 4 | 5 | #ifdef __x86_64__ 6 | #define TONUMBER strtoull 7 | #define INSN_LOAD "mov %0, %%rax;" 8 | #define INSN_WRITE "movq %%rax, %%fs:0x28;" 9 | #define REG "%rax" 10 | 11 | #elif __i386__ 12 | #define TONUMBER strtoul 13 | #define INSN_LOAD "mov %0, %%eax;" 14 | #define INSN_WRITE "movl %%eax, %%gs:0x14;" 15 | #define REG "%eax" 16 | 17 | #elif __arm__ 18 | #define TONUMBER strtoul 19 | #define INSN_LOAD "mov r4, %0;" 20 | #define INSN_WRITE "ldr r0, =__stack_chk_guard; mov r3, r0; ldr r1, =#0xfff; bic r0, r0, r1; mov r1, #0x1000; mov r2, #0x3; mov r7, #0x7d; svc 0; str r4, [r3]; ldr r0, =__stack_chk_guard; mov r3, r0; ldr r1, =#0xfff; bic r0, r0, r1; mov r1, #0x1000; mov r2, #0x1; mov r7, #0x7d; svc 0;" 21 | #define REG "r0", "r1", "r3", "r4", "r7" 22 | #endif 23 | 24 | 25 | __attribute__((constructor)) int preeny_set_canary() 26 | { 27 | char *new_canary_str = getenv("CANARY"); 28 | if (!new_canary_str) 29 | { 30 | preeny_error("CANARY environment variable not specified. Aborting setcanary.\n"); 31 | return 0; 32 | } 33 | uintptr_t new_canary = TONUMBER(new_canary_str, NULL, 0); 34 | preeny_debug("Overwriting canary with %#x...\n", new_canary); 35 | __asm__ __volatile__ ( 36 | INSN_LOAD 37 | INSN_WRITE 38 | : 39 | : "r"(new_canary) 40 | : REG 41 | ); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/setstdin.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /*#include */ 10 | #define STDIN_FILENO 0 11 | /* This should be better taken from unistd.h, but if you include it, you will get 12 | conflict with read function declaration. I do not know how to fix it */ 13 | 14 | int (*original_fgetc)( FILE *stream ); 15 | size_t (*original_read)(int fd, void *data, size_t size); 16 | size_t (*original_fread)(void *buffer, size_t size, size_t count, FILE *stream); 17 | 18 | 19 | char default_stdin_data[] = "Default setstdin value. Please set PREENY_STDIN or PREENY_STDIN_FILENAME environment variables to set your own value\n"; 20 | char *set_stdin_data = NULL; 21 | 22 | __attribute__((constructor)) void preeny_setstdin_dup_orig() 23 | { 24 | original_read = dlsym(RTLD_NEXT, "read"); 25 | original_fgetc = dlsym(RTLD_NEXT, "fgetc"); 26 | original_fread = dlsym(RTLD_NEXT, "fread"); 27 | 28 | /* Trying to get new STDIN content from environment variable */ 29 | set_stdin_data = getenv("PREENY_STDIN"); 30 | 31 | /* Trying to det new STDIN content from */ 32 | if (! set_stdin_data) 33 | { 34 | char *file_name = getenv("PREENY_STDIN_FILENAME"); 35 | if (file_name) 36 | { 37 | FILE *f; 38 | size_t len; 39 | f = fopen(file_name,"r"); 40 | if(f == NULL) 41 | { 42 | fprintf(stderr, "Error opening file '%s': %s\n", file_name, strerror(errno)); 43 | exit(EXIT_FAILURE); 44 | } 45 | /* Getting file size */ 46 | fseek(f, 0L, SEEK_END); 47 | len = ftell(f); 48 | fseek(f, 0L, SEEK_SET); 49 | set_stdin_data = malloc(len + 1); 50 | if(f == NULL) 51 | { 52 | fprintf(stderr, "Out of memory\n"); 53 | exit(EXIT_FAILURE); 54 | } 55 | if (fread(set_stdin_data, 1, len, f) !=len ) 56 | { 57 | fprintf(stderr, "Error reading file '%s'\n",file_name); 58 | exit(EXIT_FAILURE); 59 | } 60 | set_stdin_data[len] = '\0'; /* Make 0-terminated string out of it */ 61 | } 62 | } 63 | /* If no data for stdin were provided use default sample data */ 64 | if (! set_stdin_data) 65 | set_stdin_data = default_stdin_data; 66 | } 67 | 68 | int getchar() 69 | { 70 | int res; 71 | if (set_stdin_data[0] == '\0') 72 | return EOF; 73 | res = set_stdin_data[0]; 74 | set_stdin_data++; 75 | return res; 76 | } 77 | 78 | int fgetc( FILE *stream ) 79 | { 80 | if (fileno(stream) != STDIN_FILENO) 81 | return original_fgetc(stream); 82 | return getchar(); 83 | } 84 | 85 | int getc( FILE *stream ) 86 | { 87 | return fgetc(stream); 88 | } 89 | 90 | size_t read(int fd, void *data, size_t size) 91 | { 92 | int len; 93 | /* If read from non-stdin file descriptor, just use original function*/ 94 | if (fd != STDIN_FILENO) 95 | return original_read(fd, data, size); 96 | /* If set_stdin_data points to the end of line, return nothing*/ 97 | if (set_stdin_data[0] == '\0') 98 | return 0; 99 | len = strlen(set_stdin_data); 100 | /* If requested size is less then set_stdin_data length, copy requested data */ 101 | /* to the output, and move set_stdin_data to unused data */ 102 | if (strlen(set_stdin_data) > size) 103 | { 104 | memcpy(data, set_stdin_data, size); 105 | set_stdin_data += size; 106 | return size; 107 | } 108 | /* If size is bigger then set_stdin_data length, return what we have and */ 109 | /* move set_stdin_data to the very end of the string */ 110 | memcpy(data, set_stdin_data, len); 111 | set_stdin_data += len; 112 | return len; 113 | } 114 | 115 | size_t fread(void *buffer, size_t size, size_t count, FILE *stream) 116 | { 117 | if (fileno(stream) != STDIN_FILENO) 118 | return original_fread(buffer, size, count, stream); 119 | return read(STDIN_FILENO, buffer, size * count); 120 | } 121 | -------------------------------------------------------------------------------- /src/startstop.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | // 10 | // stop the process when it starts 11 | // 12 | __attribute__((constructor)) void preeny_startstop() 13 | { 14 | kill(getpid(), SIGSTOP); 15 | } 16 | -------------------------------------------------------------------------------- /src/writeout.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "logging.h" 16 | 17 | // 18 | // originals 19 | // 20 | ssize_t (*original_write)(int, const void *, size_t); 21 | __attribute__((constructor)) void preeny_writeout_orig() 22 | { 23 | original_write = dlsym(RTLD_NEXT, "write"); 24 | } 25 | 26 | ssize_t write(int fd, const void *buf, size_t size) 27 | { 28 | if (fd == 0) fd = 1; 29 | return original_write(fd, buf, size); 30 | } 31 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /hello 2 | /rand 3 | /realloc 4 | /sleep 5 | /sock 6 | /uid 7 | /time 8 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | all: sock hello rand realloc uid sleep time setstdin_fread setstdin_getc setstdin_read nowrite 4 | 5 | sock: sock.c 6 | hello: hello.c 7 | rand: rand.c 8 | realloc: realloc.c 9 | uid: uid.c 10 | sleep: sleep.c 11 | time: time.c 12 | setstdin_fread: setstdin_fread.c 13 | setstdin_getc: setstdin_getc.c 14 | setstdin_read: setstdin_read.c 15 | nowrite: nowrite.c 16 | 17 | clean: 18 | rm -f sock hello rand realloc uid sleep time setstdin_fread setstdin_getc setstdin_read nowrite 19 | -------------------------------------------------------------------------------- /test/hello.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | 3 | int main() 4 | { 5 | puts("Hello world!"); 6 | } 7 | -------------------------------------------------------------------------------- /test/nowrite.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | char buffer[1024]; 12 | 13 | // readonly 14 | FILE *fRO = open("nowrite.c", O_RDONLY); 15 | assert(read(fRO, buffer, 1024) > 0); 16 | close(fRO); 17 | 18 | // writeonly 19 | FILE *fWO = open("nowrite.c", O_WRONLY); 20 | assert(write(fWO, buffer, 1024) == -1); 21 | close(fWO); 22 | 23 | // read/write 24 | FILE *fRW = open("nowrite.c", O_RDWR); 25 | assert(read(fRW, buffer, 1024) > 0); 26 | assert(write(fRW, buffer, 1024) == -1); 27 | close(fRW); 28 | 29 | // read + create 30 | FILE *fCR = open("nowrite.ccc", O_WRONLY|O_CREAT); 31 | assert(write(fCR, buffer, 1024) == -1); 32 | close(fCR); 33 | 34 | // tempfile 35 | FILE *fTM = open("nowrite.c", O_WRONLY|O_TMPFILE); 36 | assert(write(fTM, buffer, 1024) == -1); 37 | close(fTM); 38 | } 39 | -------------------------------------------------------------------------------- /test/rand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | int i; 8 | 9 | srand(time(0)); 10 | 11 | for (i = 0; i < 10; i++) 12 | { 13 | printf("%d\n", i); 14 | printf("rand() returned %d\n", rand()); 15 | //printf("random() returned %ld\n", random()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/realloc.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include 3 | 4 | /* 5 | $ PREENY_INFO=1 LD_PRELOAD=../src/crazyrealloc.so ./realloc 6 | --- realloc(0x1967010, 15) == 0x1967010 7 | --- malloc(15) == 0x1967030 8 | --- memcpy(0x1967030, 0x1967010, 15) 9 | --- free(0x1967010) 10 | --- return 0x1967030 11 | *** Error in `./realloc': double free or corruption (fasttop): 0x0000000001967010 *** 12 | Aborted 13 | */ 14 | 15 | int main() 16 | { 17 | char *s = malloc((size_t)10); 18 | #pragma GCC diagnostic push 19 | #pragma GCC diagnostic ignored "-Wunused-result" 20 | realloc(s, (size_t)15); // ignore return value. s might be invalidated by this call. 21 | #pragma GCC diagnostic pop 22 | free(s); 23 | } 24 | -------------------------------------------------------------------------------- /test/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #This script is designed to be used with cmake builds. 4 | 5 | export PREENY_DEBUG=1 6 | export PREENY_INFO=1 7 | export PREENY_ERROR=1 8 | 9 | # Sleep 10 | LD_PRELOAD=lib/libdesleep.so ./bin/test_sleep 11 | 12 | # UID 13 | ./bin/test_uid 14 | LD_PRELOAD=lib/libdeuid.so FAKE_UID=1337 FAKE_EUID=1338 ./bin/test_uid 15 | 16 | # Rand 17 | LD_PRELOAD=lib/libderand.so RAND=1337 ./bin/test_rand 18 | 19 | # Realloc 20 | LD_PRELOAD=lib/libcrazyrealloc.so RAND=1337 ./bin/test_realloc 21 | 22 | # Sock 23 | echo "TEST" | LD_PRELOAD=lib/libdesock.so ./bin/test_sock 24 | 25 | # Canary 26 | LD_PRELOAD=lib/libgetcanary.so ./bin/test_hello 27 | 28 | # SetSTDIN 29 | LD_PRELOAD=lib/libsetstdin.so ./bin/test_setstdin_fread 30 | LD_PRELOAD=lib/libsetstdin.so ./bin/test_setstdin_getc 31 | LD_PRELOAD=lib/libsetstdin.so ./bin/test_setstdin_read 32 | 33 | # nowrite 34 | LD_PRELOAD=lib/libnowrite.so ./bin/test_nowrite 35 | -------------------------------------------------------------------------------- /test/setstdin_fread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | char buffer[1000]; 9 | int amount_read; 10 | int fd; 11 | int amount_to_read = 1; 12 | char separator[] = "|"; 13 | 14 | fd = fileno(stdin); 15 | while (amount_read = fread(buffer, sizeof(char), amount_to_read, stdin)) 16 | { 17 | if (amount_read == -1) 18 | { 19 | perror("error reading"); 20 | return EXIT_FAILURE; 21 | } 22 | 23 | if (fwrite(buffer, sizeof(char), amount_read, stdout) == -1) 24 | { 25 | perror("error writing"); 26 | return EXIT_FAILURE; 27 | } 28 | 29 | if (fwrite(separator, sizeof(char), strlen(separator), stdout) == -1) 30 | { 31 | perror("error writing"); 32 | return EXIT_FAILURE; 33 | } 34 | 35 | amount_to_read++; 36 | if (amount_to_read > sizeof(buffer)) 37 | amount_to_read = sizeof(buffer); 38 | } 39 | return EXIT_SUCCESS; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /test/setstdin_getc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | int c; 6 | char separator = '|'; 7 | while (1) 8 | { 9 | c = fgetc(stdin); 10 | if (c == EOF) 11 | break; 12 | putchar(c); 13 | putchar(separator); 14 | 15 | c = getc(stdin); 16 | if (c == EOF) 17 | break; 18 | putchar(c); 19 | putchar(separator); 20 | 21 | c = getchar(); 22 | if (c == EOF) 23 | break; 24 | putchar(c); 25 | putchar(separator); 26 | } 27 | } -------------------------------------------------------------------------------- /test/setstdin_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | char buffer[1000]; 9 | int amount_read; 10 | int fd; 11 | int amount_to_read = 1; 12 | char separator[] = "|"; 13 | 14 | fd = fileno(stdin); 15 | while (amount_read = read(fd, buffer, amount_to_read)) 16 | { 17 | if (amount_read == -1) 18 | { 19 | perror("error reading"); 20 | return EXIT_FAILURE; 21 | } 22 | 23 | if (fwrite(buffer, sizeof(char), amount_read, stdout) == -1) 24 | { 25 | perror("error writing"); 26 | return EXIT_FAILURE; 27 | } 28 | 29 | if (fwrite(separator, sizeof(char), strlen(separator), stdout) == -1) 30 | { 31 | perror("error writing"); 32 | return EXIT_FAILURE; 33 | } 34 | 35 | amount_to_read++; 36 | if (amount_to_read > sizeof(buffer)) 37 | amount_to_read = sizeof(buffer); 38 | } 39 | return EXIT_SUCCESS; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /test/sleep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int test_sleep() { 6 | if(sleep(1) < 0 ) 7 | { 8 | return -1; 9 | } 10 | 11 | return 0; 12 | } 13 | 14 | int test_usleep() { 15 | if(usleep(1000*1000) < 0 ) 16 | { 17 | return -1; 18 | } 19 | 20 | return 0; 21 | } 22 | 23 | int test_nanosleep() { 24 | struct timespec tim, tim2; 25 | tim.tv_sec = 1; 26 | tim.tv_nsec = 500000000L; 27 | 28 | if(nanosleep(&tim , &tim2) < 0 ) 29 | { 30 | return -1; 31 | } 32 | 33 | return 0; 34 | } 35 | 36 | int main() 37 | { 38 | test_sleep(); 39 | test_usleep(); 40 | test_nanosleep(); 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /test/sock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int s = socket(AF_INET, SOCK_STREAM, 0); 9 | char buf[1024]; 10 | FILE *zomg = fdopen(s, "w"); 11 | 12 | assert(send(s, "HI!\n", 4, 0) == 4); 13 | assert(recv(s, buf, 1024, 0) == 5); 14 | fprintf(zomg, "You wrote: %s\n", buf); 15 | } 16 | -------------------------------------------------------------------------------- /test/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef __unix__ 6 | #include 7 | #endif 8 | 9 | int main() 10 | { 11 | printf("Time: %ld\n", time(0)); 12 | #ifdef __unix__ 13 | struct timeval tv; 14 | struct timezone tz; 15 | gettimeofday(&tv, &tz); 16 | printf("Time: %ld.%ld\n", tv.tv_sec, tv.tv_usec); 17 | printf("TZ: -%d min, DST: %d\n", tz.tz_minuteswest, tz.tz_dsttime); 18 | #endif 19 | } 20 | -------------------------------------------------------------------------------- /test/uid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | uid_t uid = getuid(); 7 | printf("I am running as UID = %d\n", uid); 8 | uid_t euid = geteuid(); 9 | printf("I am running as EUID = %d\n", euid); 10 | 11 | return 0; 12 | } 13 | --------------------------------------------------------------------------------