├── patches ├── cves.gif ├── demo.gif ├── ghost_demo.gif ├── Makefile ├── find_nm_info ├── upatch.h ├── template │ ├── patches.c │ └── Makefile ├── ideas │ └── fentry │ │ ├── fentry_override.c │ │ └── fentry_override.md ├── gen_priv_funcs ├── CVE-2015-0235 │ ├── GHOST.c │ ├── Makefile │ └── patches.c ├── CVE-2015-4000 │ └── Makefile ├── CVE-2016-5423 │ └── Makefile ├── gen_trampolines ├── CVE-2015-5477 │ └── Makefile ├── CVE-2016-0773 │ ├── Makefile │ └── patches.c ├── CVE-2015-3185 │ ├── Makefile │ └── patches.c ├── README.md ├── upatch.c ├── GHOST.md └── CVE-2015-0240 │ ├── Makefile │ └── patches.c ├── slides_BHArsenal2015.pdf ├── upatch_status ├── utils.h ├── gdb_inject ├── upatch ├── sample-library.c ├── .gitignore ├── ptrace.h ├── sample-target.c ├── who_linked ├── Makefile ├── README.md ├── utils.c ├── unject-x86_64.c ├── ptrace.c ├── inject-x86.c ├── inject-arm.c ├── inject-x86_64.c └── LICENSE.txt /patches/cves.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joe-lawrence/linux-inject/HEAD/patches/cves.gif -------------------------------------------------------------------------------- /patches/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joe-lawrence/linux-inject/HEAD/patches/demo.gif -------------------------------------------------------------------------------- /patches/ghost_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joe-lawrence/linux-inject/HEAD/patches/ghost_demo.gif -------------------------------------------------------------------------------- /slides_BHArsenal2015.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joe-lawrence/linux-inject/HEAD/slides_BHArsenal2015.pdf -------------------------------------------------------------------------------- /upatch_status: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for p in /proc/[0-9]*; do 4 | 5 | libs=$(grep -o 'libupatch_.*.so' $p/maps 2>/dev/null | sort -u | tr '\n' ' ') 6 | [[ "$libs" ]] && echo "$(basename $(readlink -f $p/exe))($(basename $p)) => $libs" 7 | 8 | done 9 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #define INTEL_RET_INSTRUCTION 0xc3 2 | #define INTEL_INT3_INSTRUCTION 0xcc 3 | 4 | pid_t findProcessByName(char* processName); 5 | long freespaceaddr(pid_t pid); 6 | long getlibcaddr(pid_t pid); 7 | int checkloaded(pid_t pid, char* libname); 8 | long getFunctionAddress(char* funcName); 9 | unsigned char* findRet(void* endAddr); 10 | void usage(char* name); 11 | -------------------------------------------------------------------------------- /gdb_inject: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -lt 2 ]; then 4 | echo "gdb_inject.sh " 5 | echo 6 | exit 7 | fi 8 | 9 | script=$(mktemp) 10 | 11 | echo "attach $1" >> $script 12 | echo "call (void *) __libc_dlopen_mode(\"$2\", 1)" >> $script 13 | echo "detach" >> $script 14 | 15 | echo "q" >> $script 16 | 17 | gdb -q --command=$script 18 | rm $script 19 | -------------------------------------------------------------------------------- /patches/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 2 | SRC=upatch.c 3 | 4 | -include $(SRC:.c=.d) 5 | 6 | all: libupatch.so 7 | 8 | libupatch.so: $(SRC:.c=.o) 9 | $(CC) -shared -lsystemd -lunwind -Wl,-soname,$@ -o $@ $^ 10 | 11 | install: 12 | install -D libupatch.so /usr/lib64 13 | install -D upatch.h /usr/include/upatch.h 14 | ldconfig -n /usr/lib64 15 | 16 | uninstall: 17 | $(RM) /usr/lib64/libupatch.so 18 | ldconfig -n /usr/lib64 19 | 20 | clean: 21 | $(RM) -f *.d *.o *.so 22 | 23 | .PHONY: install uninstall clean 24 | -------------------------------------------------------------------------------- /patches/find_nm_info: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$#" -ne 2 ]]; then 4 | echo "Usage:" 5 | echo " find_nm_info " 6 | echo 7 | echo " e.g. find_nm_info httpd-debuginfo ap_process_request_internal" 8 | echo 9 | exit 1 10 | fi 11 | 12 | rpm -qi $1 &>/dev/null || (echo "$1 not installed!" && exit 1) 13 | 14 | for f in $(rpm -ql "$1" | grep -v '\.build-id'); do 15 | info=$(readelf -a --wide $f | grep "$2") 16 | [[ "$info" ]] && echo -e "$f\n$info\n"; 17 | done 2>/dev/null 18 | 19 | -------------------------------------------------------------------------------- /upatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -lt 2 ]; then 4 | echo "upatch " 5 | echo 6 | echo " e.g. upatch /usr/lib64/libupatch_cve_test.so 17744 17745" 7 | echo 8 | exit 9 | fi 10 | 11 | l=$1 12 | shift 13 | 14 | for p in $@; do 15 | 16 | [[ "$p" == "1" ]] && continue; 17 | 18 | u=$(grep "$l" /proc/$p/maps 2>/dev/null) 19 | if [[ "$u" ]]; then 20 | echo "$p already patched by $l" 21 | continue 22 | fi 23 | 24 | #./inject -p $p $l 25 | ./gdb_inject $p $l 26 | 27 | done 28 | -------------------------------------------------------------------------------- /sample-library.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | * hello() 6 | * 7 | * Hello world function exported by the sample library. 8 | * 9 | */ 10 | 11 | void hello() 12 | { 13 | printf("I just got loaded\n"); 14 | } 15 | 16 | /* 17 | * loadMsg() 18 | * 19 | * This function is automatically called when the sample library is injected 20 | * into a process. It calls hello() to output a message indicating that the 21 | * library has been loaded. 22 | * 23 | */ 24 | 25 | __attribute__((constructor)) 26 | void loadMsg() 27 | { 28 | hello(); 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # GCC dependency files 24 | *.d 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | *.i*86 31 | *.x86_64 32 | *.hex 33 | 34 | # Specific binary names 35 | unject 36 | inject 37 | inject32 38 | sample-target 39 | sample-target32 40 | sample-library.so 41 | sample-library32.so 42 | 43 | # Debugging files 44 | peda-session-*.txt 45 | .gdb_history 46 | core* 47 | -------------------------------------------------------------------------------- /ptrace.h: -------------------------------------------------------------------------------- 1 | #ifdef ARM 2 | #define REG_TYPE user_regs 3 | #else 4 | #define REG_TYPE user_regs_struct 5 | #endif 6 | 7 | void ptrace_attach(pid_t target); 8 | void ptrace_detach(pid_t target); 9 | void ptrace_getregs(pid_t target, struct REG_TYPE* regs); 10 | void ptrace_cont(pid_t target); 11 | void ptrace_setregs(pid_t target, struct REG_TYPE* regs); 12 | siginfo_t ptrace_getsiginfo(pid_t target); 13 | void ptrace_read(int pid, unsigned long addr, void *vptr, int len); 14 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len); 15 | void checktargetsig(int pid); 16 | void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs); 17 | -------------------------------------------------------------------------------- /patches/upatch.h: -------------------------------------------------------------------------------- 1 | #ifndef __TRAMPOLINE_H__ 2 | # define __TRAMPOLINE__ 3 | 4 | void patch_constructor(void); 5 | void patch_destructor(void); 6 | 7 | #define TRAMPOLINE_BYTES (16) 8 | #define BUILD_ID_BYTES (40) 9 | 10 | enum offset_type { T_OFFSET_ABS, T_OFFSET_REL }; 11 | 12 | struct trampolines { 13 | void *old_addr; 14 | size_t old_size; 15 | void *new_addr; 16 | const char *oldname; 17 | const char *map_name; 18 | unsigned long offset; 19 | enum offset_type type; 20 | int patched; 21 | char old_code[TRAMPOLINE_BYTES]; 22 | char build_id[BUILD_ID_BYTES+1]; 23 | }; 24 | 25 | void libupatch_load(struct trampolines []); 26 | void libupatch_unload(struct trampolines []); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /patches/template/patches.c: -------------------------------------------------------------------------------- 1 | /* Project header files */ 2 | 3 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 4 | 5 | /* Resolve any private symols to the target binary */ 6 | #include "upatch_private.h" 7 | 8 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 9 | 10 | /* Private copy of any necessary typedefs, macros, or 11 | * inlined functions not inclued by the project headers */ 12 | 13 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 14 | /* Patched function() * * * * * * * * * * * * * * * * * * * * * * */ 15 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 16 | 17 | /* The patched function must be prefixed by "patched_" */ 18 | -------------------------------------------------------------------------------- /sample-target.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * sleepfunc() 8 | * 9 | * The only purpose of this function is to output the message "sleeping..." 10 | * once a second to provide a more concrete idea of when the sample library 11 | * gets injected. 12 | * 13 | */ 14 | 15 | void sleepfunc() 16 | { 17 | struct timespec* sleeptime = malloc(sizeof(struct timespec)); 18 | 19 | sleeptime->tv_sec = 1; 20 | sleeptime->tv_nsec = 0; 21 | 22 | while(1) 23 | { 24 | printf("sleeping...\n"); 25 | nanosleep(sleeptime, NULL); 26 | } 27 | 28 | free(sleeptime); 29 | } 30 | 31 | /* 32 | * main() 33 | * 34 | * Call sleepfunc(), which loops forever. 35 | * 36 | */ 37 | 38 | int main() 39 | { 40 | sleepfunc(); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /patches/ideas/fentry/fentry_override.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | gcc fentry_override.c -pg -mfentry -c -o fentry_override.o 4 | gcc -o fentry_override fentry_override.o 5 | 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #define FENTRY_BYTES (5) 12 | 13 | int my_patch(int a) 14 | { 15 | printf("my_patch: %d\n", a); 16 | } 17 | 18 | int my_function(int a) 19 | { 20 | printf("my function: %d!\n", a); 21 | } 22 | 23 | __attribute__((no_instrument_function)) 24 | void __fentry__(void) 25 | { 26 | uint64_t returnaddr; 27 | 28 | asm("mov 8(%%rbp),%0" : "=r"(returnaddr) : : ); 29 | 30 | if (returnaddr == (uint64_t) my_function + FENTRY_BYTES) { 31 | returnaddr = (uint64_t) my_patch + FENTRY_BYTES; 32 | asm("mov %0,8(%%rbp)" : "=r"(returnaddr) : : ); 33 | } 34 | } 35 | 36 | int main() 37 | { 38 | my_function(123); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /who_linked: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ] && [ "$#" -ne 2 ]; then 4 | echo "who_linked [-f] " 5 | echo 6 | exit 7 | fi 8 | 9 | if [[ "$1" == "-f" ]]; then 10 | arg_full=true 11 | shift 12 | fi 13 | 14 | arg_lib=$(readlink -f $1) 15 | if [[ ! -e $arg_lib ]] ; then 16 | echo "Can't find $arg_lib" 17 | exit 1 18 | fi 19 | 20 | 21 | for p in /proc/[0-9]*; do 22 | 23 | l=$(grep $arg_lib $p/maps 2>/dev/null) 24 | u=$(grep "$arg_lib" $p/maps 2>/dev/null) 25 | 26 | [[ "$l" ]] || continue 27 | 28 | [[ "$arg_full" = true ]] && info="$(basename $(readlink -f $p/exe))($(basename $p))" \ 29 | || info="$(basename $p)" 30 | 31 | if [[ "$l" && "$u" ]] ; then 32 | echo -n "$info " 33 | 34 | if [[ "$arg_full" = true ]]; then 35 | libs=$(grep -o 'libupatch_.*.so' $p/maps 2>/dev/null | sort -u | tr '\n' ' ') 36 | [[ "$libs" ]] && echo -n " => $libs" 37 | echo 38 | fi 39 | fi 40 | 41 | done 42 | echo 43 | -------------------------------------------------------------------------------- /patches/gen_priv_funcs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -a MAP_NAMES=() 4 | declare -a DEBUG_NAMES=() 5 | declare -a RETURN_TYPES=() 6 | declare -a ENTRY_POINTS=() 7 | 8 | while (( "$#" )); do 9 | 10 | MAP_NAMES+=($(readlink -f $1)) 11 | DEBUG_NAMES+=($(readlink -f /usr/lib/debug/$1.debug)) 12 | RETURN_TYPES+=("$2") 13 | ENTRY_POINTS+=($3) 14 | shift 15 | shift 16 | shift 17 | 18 | done 19 | 20 | 21 | i=0; 22 | for map_name in "${MAP_NAMES[@]}"; do 23 | 24 | debug_name=${DEBUG_NAMES[i]} 25 | entry_name=${ENTRY_POINTS[i]} 26 | 27 | addr=$(nm -S $debug_name 2>/dev/null | awk "/\<$entry_name\>/{print \"0x\" \$1}") 28 | if [[ -z "$addr" ]] ; then 29 | addr=$(nm -S $map_name 2>/dev/null | awk "/\<$entry_name\>/{print \"0x\" \$1}") 30 | from=$map_name 31 | else 32 | from=$debug_name 33 | fi 34 | 35 | return_type=${RETURN_TYPES[i]} 36 | 37 | cat < $@ 32 | 33 | upatch_private.h: 34 | ../gen_priv_funcs ${PRIV_FUNCS} > $@ 35 | 36 | install: all 37 | install -D libupatch_$(UPATCH).so /usr/lib64 38 | ldconfig -n /usr/lib64 39 | 40 | uninstall: 41 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 42 | ldconfig -n /usr/lib64 43 | 44 | clean: 45 | $(RM) -f trampolines.c upatch_private.h *.d *.o *.so 46 | 47 | .PHONY: install uninstall clean 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | UNAME_M := $(shell uname -m) 2 | 3 | .PHONY: x86 x86_64 arm 4 | 5 | all: 6 | ifeq ($(UNAME_M),x86_64) 7 | $(MAKE) x86_64 8 | endif 9 | ifeq ($(UNAME_M),x86) 10 | $(MAKE) x64 11 | endif 12 | ifneq (,$(findstring arm,$(UNAME_M))) 13 | $(MAKE) arm 14 | endif 15 | 16 | 17 | arm: sample-target sample-library.so 18 | $(CC) -marm $(CFLAGS) -DARM -o inject utils.c ptrace.c inject-arm.c -ldl 19 | 20 | x86: sample-target sample-library.so 21 | $(CC) $(CFLAGS) -o inject utils.c ptrace.c inject-x86.c -ldl 22 | 23 | x86_64: 24 | $(CC) $(CFLAGS) -o inject utils.c ptrace.c inject-x86_64.c -ldl 25 | $(CC) $(CFLAGS) -o unject utils.c ptrace.c unject-x86_64.c -ldl 26 | $(CC) $(CFLAGS) -D_GNU_SOURCE -shared -o sample-library.so -fPIC sample-library.c 27 | $(CC) $(CFLAGS) -o sample-target sample-target.c 28 | $(CC) -m32 $(CFLAGS) -o inject32 utils.c ptrace.c inject-x86.c -ldl 29 | $(CC) -m32 $(CFLAGS) -D_GNU_SOURCE -shared -o sample-library32.so -fPIC sample-library.c 30 | $(CC) -m32 $(CFLAGS) -o sample-target32 sample-target.c 31 | 32 | sample-library.so: sample-library.c 33 | $(CC) $(CFLAGS) -D_GNU_SOURCE -shared -o sample-library.so -fPIC sample-library.c 34 | 35 | sample-target: sample-target.c 36 | $(CC) $(CFLAGS) -o sample-target sample-target.c 37 | 38 | clean: 39 | rm -f sample-library.so 40 | rm -f sample-target 41 | rm -f unject inject 42 | rm -f sample-library32.so 43 | rm -f sample-target32 44 | rm -f inject32 45 | -------------------------------------------------------------------------------- /patches/CVE-2015-0235/GHOST.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GHOST vulnerability check 3 | * http://www.openwall.com/lists/oss-security/2015/01/27/9 4 | * Usage: gcc GHOST.c -o GHOST && ./GHOST 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define CANARY "in_the_coal_mine" 15 | 16 | struct { 17 | char buffer[1024]; 18 | char canary[sizeof(CANARY)]; 19 | } temp = { "buffer", CANARY }; 20 | 21 | int main(void) { 22 | 23 | int i; 24 | 25 | for (i=0; ; i++) { 26 | 27 | struct hostent resbuf; 28 | struct hostent *result; 29 | int herrno; 30 | int retval; 31 | 32 | /*** strlen (name) = size_needed - sizeof (*host_addr) - sizeof (*h_addr_ptrs) - 1; ***/ 33 | size_t len = sizeof(temp.buffer) - 16*sizeof(unsigned char) - 2*sizeof(char *) - 1; 34 | char name[sizeof(temp.buffer)]; 35 | memset(name, '0', len); 36 | name[len] = '\0'; 37 | 38 | retval = gethostbyname_r(name, &resbuf, temp.buffer, sizeof(temp.buffer), &result, &herrno); 39 | 40 | if (strcmp(temp.canary, CANARY) != 0) { 41 | printf("%d\t\e[31mvulnerable\e[0m\n", i); 42 | } 43 | if (retval == ERANGE) { 44 | printf("%d\t\e[32mnot vulnerable\e[0m\n", i); 45 | } 46 | 47 | sleep(5); 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /patches/CVE-2015-4000/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # upatch specifics 3 | # 4 | # based on: 5 | # openssl-1.0.1e-42.el7_1.5.src.rpm 6 | 7 | UPATCH=cve_2015_4000 8 | TRAMPOLINES := \ 9 | /usr/lib64/libssl.so ssl3_check_cert_and_algorithm 10 | 11 | PATCH_SRC=patches.c 12 | 13 | patches.o: patches.c 14 | $(CC) \ 15 | -I/root/rpmbuild/BUILD/openssl-1.0.1e/crypto \ 16 | -I/root/rpmbuild/BUILD/openssl-1.0.1e \ 17 | -I/root/rpmbuild/BUILD/openssl-1.0.1e/include \ 18 | -I/root/rpmbuild/BUILD/openssl-1.0.1e/ssl \ 19 | -I/usr/include \ 20 | -fPIC \ 21 | -DOPENSSL_PIC \ 22 | -DZLIB \ 23 | -DOPENSSL_THREADS \ 24 | -D_REENTRANT \ 25 | -DDSO_DLFCN \ 26 | -DHAVE_DLFCN_H \ 27 | -DKRB5_MIT \ 28 | -m64 \ 29 | -DL_ENDIAN \ 30 | -DTERMIO \ 31 | -Wall \ 32 | -O2 \ 33 | -g \ 34 | -pipe \ 35 | -Wall \ 36 | -Wp,-D_FORTIFY_SOURCE=2 \ 37 | -fexceptions \ 38 | -fstack-protector-strong \ 39 | --param=ssp-buffer-size=4 \ 40 | -grecord-gcc-switches \ 41 | -m64 \ 42 | -mtune=generic \ 43 | -Wa,--noexecstack \ 44 | -DPURIFY \ 45 | -DOPENSSL_IA32_SSE2 \ 46 | -DOPENSSL_BN_ASM_MONT \ 47 | -DOPENSSL_BN_ASM_MONT5 \ 48 | -DOPENSSL_BN_ASM_GF2m \ 49 | -DSHA1_ASM \ 50 | -DSHA256_ASM \ 51 | -DSHA512_ASM \ 52 | -DMD5_ASM \ 53 | -DAES_ASM \ 54 | -DVPAES_ASM \ 55 | -DBSAES_ASM \ 56 | -DWHIRLPOOL_ASM \ 57 | -DGHASH_ASM \ 58 | -c \ 59 | $< \ 60 | -o \ 61 | $@ 62 | 63 | # 64 | # upatch boilerplate 65 | # 66 | 67 | .DEFAULT_GOAL:=all 68 | all: libupatch_$(UPATCH).so 69 | 70 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 71 | 72 | libupatch_$(UPATCH).so: $(PATCH_SRC:.c=.o) trampolines.o 73 | $(CC) -shared -lupatch -lsystemd -Wl,-soname,$@ -o $@ $^ 74 | 75 | -include trampolines.d 76 | 77 | trampolines.c: 78 | ../gen_trampolines ${TRAMPOLINES} > $@ 79 | 80 | install: all 81 | install -D libupatch_$(UPATCH).so /usr/lib64 82 | ldconfig -n /usr/lib64 83 | 84 | uninstall: 85 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 86 | ldconfig -n /usr/lib64 87 | 88 | clean: 89 | $(RM) -f trampolines.c *.d *.o *.so 90 | 91 | .PHONY: install uninstall clean 92 | -------------------------------------------------------------------------------- /patches/CVE-2016-5423/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # upatch specifics 3 | # 4 | 5 | UPATCH=cve_2016_5423 6 | TRAMPOLINES := \ 7 | /usr/bin/postgres ExecEvalCase \ 8 | /usr/bin/postgres simplify_function \ 9 | 10 | PRIV_FUNCS := \ 11 | /usr/bin/postgres void sql_inline_error_callback \ 12 | /usr/bin/postgres "Node *" substitute_actual_parameters_mutator \ 13 | /usr/bin/postgres "Node *" eval_const_expressions_mutator \ 14 | /usr/bin/postgres "List *" expand_function_arguments \ 15 | 16 | PATCH_SRC=patches.c 17 | 18 | patches.o: patches.c upatch_private.h 19 | $(CC) \ 20 | -fPIC \ 21 | -O2 \ 22 | -g \ 23 | -pipe \ 24 | -Wall \ 25 | -Wp,-D_FORTIFY_SOURCE=2 \ 26 | -fexceptions \ 27 | -fstack-protector-strong \ 28 | --param=ssp-buffer-size=4 \ 29 | -grecord-gcc-switches \ 30 | -m64 \ 31 | -mtune=generic \ 32 | -DLINUX_OOM_SCORE_ADJ=0 \ 33 | -Wall \ 34 | -Wmissing-prototypes \ 35 | -Wpointer-arith \ 36 | -Wdeclaration-after-statement \ 37 | -Wendif-labels \ 38 | -Wmissing-format-attribute \ 39 | -Wformat-security \ 40 | -fno-strict-aliasing \ 41 | -fwrapv \ 42 | -fexcess-precision=standard \ 43 | -I/root/rpmbuild/BUILD/postgresql-9.2.14/src/include \ 44 | -D_GNU_SOURCE \ 45 | -I/usr/include/libxml2 \ 46 | -c \ 47 | -o \ 48 | patches.o \ 49 | patches.c 50 | 51 | # 52 | # upatch boilerplate 53 | # 54 | 55 | .DEFAULT_GOAL:=all 56 | all: libupatch_$(UPATCH).so 57 | 58 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 59 | 60 | libupatch_$(UPATCH).so: $(PATCH_SRC:.c=.o) trampolines.o 61 | $(CC) -shared -lupatch -lsystemd -Wl,-soname,$@ -o $@ $^ 62 | 63 | -include trampolines.d 64 | 65 | trampolines.c: 66 | ../gen_trampolines ${TRAMPOLINES} > $@ 67 | 68 | upatch_private.h: 69 | ../gen_priv_funcs ${PRIV_FUNCS} > $@ 70 | 71 | install: all 72 | install -D libupatch_$(UPATCH).so /usr/lib64 73 | ldconfig -n /usr/lib64 74 | 75 | uninstall: 76 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 77 | ldconfig -n /usr/lib64 78 | 79 | clean: 80 | $(RM) -f trampolines.c upatch_private.h *.d *.o *.so 81 | 82 | .PHONY: install uninstall clean 83 | -------------------------------------------------------------------------------- /patches/gen_trampolines: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -a MAP_NAMES=() 4 | declare -a DEBUG_NAMES=() 5 | declare -a ENTRY_POINTS=() 6 | 7 | while (( "$#" )); do 8 | 9 | MAP_NAMES+=($(readlink -f $1)) 10 | DEBUG_NAMES+=($(readlink -f /usr/lib/debug/$1.debug)) 11 | ENTRY_POINTS+=($2) 12 | shift 13 | shift 14 | 15 | done 16 | 17 | 18 | echo "#include " 19 | echo "#include " 20 | echo "" 21 | 22 | i=0; 23 | for map_name in "${MAP_NAMES[@]}"; do 24 | old_name=${ENTRY_POINTS[i]} 25 | echo "extern void *patched_$old_name;" 26 | ((i++)) 27 | done 28 | echo "" 29 | 30 | echo "struct trampolines tramps[] = {" 31 | echo "" 32 | i=0; 33 | for map_name in "${MAP_NAMES[@]}"; do 34 | 35 | debug_name=${DEBUG_NAMES[i]} 36 | 37 | build_id=$(readelf -n $debug_name 2>/dev/null | awk '/Build/{print $NF}') 38 | [[ -z "$build_id" ]] && build_id=$(readelf -n $map_name 2>/dev/null | awk '/Build/{print $NF}') 39 | 40 | old_name=${ENTRY_POINTS[i]} 41 | 42 | old_offset=$(nm -S $debug_name 2>/dev/null | awk "/\<$old_name\>/{print \$1}") 43 | [[ -z "$old_offset" ]] && old_offset=$(nm -S $map_name 2>/dev/null | awk "/\<$old_name\>/{print \$1}") 44 | 45 | old_size=$(nm -S $debug_name 2>/dev/null | awk "/\<$old_name\>/{print \$2}") 46 | [[ -z "$old_size" ]] && old_size=$(nm -S $map_name 2>/dev/null | awk "/\<$old_name\>/{print \$2}") 47 | 48 | file $map_name | grep -q 'shared object' && offset="T_OFFSET_REL" || offset="T_OFFSET_ABS" 49 | 50 | cat < $@ 78 | 79 | install: all 80 | install -D libupatch_$(UPATCH).so /usr/lib64 81 | ldconfig -n /usr/lib64 82 | 83 | uninstall: 84 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 85 | ldconfig -n /usr/lib64 86 | 87 | clean: 88 | $(RM) -f trampolines.c *.d *.o *.so 89 | 90 | .PHONY: install uninstall clean 91 | -------------------------------------------------------------------------------- /patches/CVE-2016-0773/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # upatch specifics 3 | # 4 | # based on postgresql-9.2.14-1.el7_1 5 | 6 | UPATCH=cve_2016_0773 7 | TRAMPOLINES := \ 8 | /usr/bin/postgres dovec \ 9 | /usr/bin/postgres lexescape \ 10 | /usr/bin/postgres range \ 11 | 12 | PRIV_FUNCS := \ 13 | /usr/bin/postgres chr chrnamed \ 14 | /usr/bin/postgres "struct cvec *" getcvec \ 15 | /usr/bin/postgres chr lexdigits \ 16 | /usr/bin/postgres void newarc \ 17 | /usr/bin/postgres color newsub \ 18 | /usr/bin/postgres int pg_wc_isalnum \ 19 | /usr/bin/postgres pg_wchar pg_wc_tolower \ 20 | /usr/bin/postgres pg_wchar pg_wc_toupper \ 21 | /usr/bin/postgres color subcolor \ 22 | 23 | PATCH_SRC=patches.c 24 | 25 | patches.o: patches.c upatch_private.h 26 | $(CC) \ 27 | -fPIC \ 28 | -O2 \ 29 | -g \ 30 | -pipe \ 31 | -Wall \ 32 | -Wp,-D_FORTIFY_SOURCE=2 \ 33 | -fexceptions \ 34 | -fstack-protector-strong \ 35 | --param=ssp-buffer-size=4 \ 36 | -grecord-gcc-switches \ 37 | -m64 \ 38 | -mtune=generic \ 39 | -DLINUX_OOM_SCORE_ADJ=0 \ 40 | -Wall \ 41 | -Wmissing-prototypes \ 42 | -Wpointer-arith \ 43 | -Wdeclaration-after-statement \ 44 | -Wendif-labels \ 45 | -Wmissing-format-attribute \ 46 | -Wformat-security \ 47 | -fno-strict-aliasing \ 48 | -fwrapv \ 49 | -fexcess-precision=standard \ 50 | -I/root/rpmbuild/BUILD/postgresql-9.2.14/src/include \ 51 | -D_GNU_SOURCE \ 52 | -I/usr/include/libxml2 \ 53 | -c \ 54 | -o \ 55 | patches.o \ 56 | patches.c 57 | 58 | # 59 | # upatch boilerplate 60 | # 61 | 62 | .DEFAULT_GOAL:=all 63 | all: libupatch_$(UPATCH).so 64 | 65 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 66 | 67 | libupatch_$(UPATCH).so: $(PATCH_SRC:.c=.o) trampolines.o 68 | $(CC) -shared -lupatch -lsystemd -Wl,-soname,$@ -o $@ $^ 69 | 70 | -include trampolines.d 71 | 72 | trampolines.c: 73 | ../gen_trampolines ${TRAMPOLINES} > $@ 74 | 75 | upatch_private.h: 76 | ../gen_priv_funcs ${PRIV_FUNCS} > $@ 77 | 78 | install: all 79 | install -D libupatch_$(UPATCH).so /usr/lib64 80 | ldconfig -n /usr/lib64 81 | 82 | uninstall: 83 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 84 | ldconfig -n /usr/lib64 85 | 86 | clean: 87 | $(RM) -f trampolines.c upatch_private.h *.d *.o *.so 88 | 89 | .PHONY: install uninstall clean 90 | -------------------------------------------------------------------------------- /patches/CVE-2015-3185/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # upatch specifics 3 | # 4 | # based on: 5 | # httpd-2.4.6-45.el7.src.rpm 6 | 7 | UPATCH=cve_2015_3185 8 | TRAMPOLINES := \ 9 | /usr/sbin/httpd ap_process_request_internal 10 | 11 | PATCH_SRC=patches.c 12 | 13 | patches.o: patches.c 14 | /usr/lib64/apr-1/build/libtool \ 15 | --silent \ 16 | --mode=compile \ 17 | gcc \ 18 | -std=gnu99 \ 19 | -pthread \ 20 | -O2 \ 21 | -g \ 22 | -pipe \ 23 | -Wall \ 24 | -Wp,-D_FORTIFY_SOURCE=2 \ 25 | -fexceptions \ 26 | -fstack-protector-strong \ 27 | --param=ssp-buffer-size=4 \ 28 | -grecord-gcc-switches \ 29 | -m64 \ 30 | -mtune=generic \ 31 | -DLINUX \ 32 | -D_REENTRANT \ 33 | -D_GNU_SOURCE \ 34 | -I/root/rpmbuild/BUILD/httpd-2.4.6 \ 35 | -I/root/rpmbuild/BUILD/httpd-2.4.6/os/unix \ 36 | -I/root/rpmbuild/BUILD/httpd-2.4.6/include \ 37 | -I/usr/include/apr-1 \ 38 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/aaa \ 39 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/cache \ 40 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/core \ 41 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/database \ 42 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/filters \ 43 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/ldap \ 44 | -I/root/rpmbuild/BUILD/httpd-2.4.6/server \ 45 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/loggers \ 46 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/lua \ 47 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/proxy \ 48 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/session \ 49 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/ssl \ 50 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/test \ 51 | -I/root/rpmbuild/BUILD/httpd-2.4.6/server \ 52 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/arch/unix \ 53 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/dav/main \ 54 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/generators \ 55 | -I/root/rpmbuild/BUILD/httpd-2.4.6/modules/mappers \ 56 | -fPIC \ 57 | -c \ 58 | $^ \ 59 | 60 | # -fPIE \ 61 | # -prefer-non-pic \ 62 | # -static \ 63 | 64 | # 65 | # upatch boilerplate 66 | # 67 | 68 | .DEFAULT_GOAL:=all 69 | all: libupatch_$(UPATCH).so 70 | 71 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 72 | 73 | libupatch_$(UPATCH).so: $(PATCH_SRC:.c=.o) trampolines.o 74 | $(CC) -shared -lupatch -lsystemd -Wl,-soname,$@ -o $@ $^ 75 | 76 | -include trampolines.d 77 | 78 | trampolines.c: 79 | ../gen_trampolines ${TRAMPOLINES} > $@ 80 | 81 | install: all 82 | install -D libupatch_$(UPATCH).so /usr/lib64 83 | ldconfig -n /usr/lib64 84 | 85 | uninstall: 86 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 87 | ldconfig -n /usr/lib64 88 | 89 | clean: 90 | $(RM) -f trampolines.c *.d *.o *.so *.lo 91 | 92 | .PHONY: install uninstall clean 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linux-inject 2 | **Tool for injecting a shared object into a Linux process** 3 | 4 | * Provides the Linux equivalent of using `CreateRemoteThread()` on Windows to inject a DLL into a running process 5 | 6 | * Performs injection using `ptrace()` rather than `LD_PRELOAD`, since the target process is already running at the time of injection 7 | 8 | * Supports x86, x86_64, and ARM 9 | 10 | * Does not require the target process to have been built with `-ldl` flag, because it loads the shared object using `__libc_dlopen_mode()` from libc rather than `dlopen()` from libdl 11 | 12 | ## Caveat about `ptrace()` 13 | 14 | * On many Linux distributions, the kernel is configured by default to prevent any process from calling `ptrace()` on another process that it did not create (e.g. via `fork()`). 15 | 16 | * This is a security feature meant to prevent exactly the kind of mischief that this tool causes. 17 | 18 | * You can temporarily disable it until the next reboot using the following command: 19 | 20 | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 21 | 22 | ## Compiling 23 | 24 | * Simply running `make` should automatically select and build for the correct architecture, but if this fails (or you would like to select the target manually), run one of the following make commands: 25 | 26 | * arm: 27 | 28 | make arm 29 | 30 | * x86: 31 | 32 | make x86 33 | 34 | * x86_64: 35 | 36 | make x86_64 37 | 38 | ## Usage 39 | 40 | ./inject [-n process-name] [-p pid] [library-to-inject] 41 | 42 | ## Sample 43 | 44 | * In one terminal, start up the sample target app, which simply outputs "sleeping..." each second: 45 | 46 | ./sample-target 47 | 48 | * In another terminal, inject sample-library.so into the target app: 49 | 50 | ./inject -n sample-target sample-library.so 51 | 52 | * The output should look something like this: 53 | 54 | * First terminal: 55 | 56 | $ ./sample-target 57 | sleeping... 58 | sleeping... 59 | I just got loaded 60 | sleeping... 61 | sleeping... 62 | 63 | * Second terminal: 64 | 65 | $ ./inject -n sample-target sample-library.so 66 | targeting process "sample-target" with pid 31490 67 | library "sample-library.so" successfully injected 68 | $ 69 | 70 | * If the injection fails, make sure your machine is configured to allow processes to `ptrace()` other processes that they did not create. See the "Caveat about `ptrace()`" section above. 71 | 72 | * You can verify that the injection was successful by checking `/proc/[pid]/maps`: 73 | 74 | $ cat /proc/$(pgrep sample-target)/maps 75 | [...] 76 | 7f37d5cc6000-7f37d5cc7000 r-xp 00000000 ca:01 267321 /home/ubuntu/linux-inject/sample-library.so 77 | 7f37d5cc7000-7f37d5ec6000 ---p 00001000 ca:01 267321 /home/ubuntu/linux-inject/sample-library.so 78 | 7f37d5ec6000-7f37d5ec7000 r--p 00000000 ca:01 267321 /home/ubuntu/linux-inject/sample-library.so 79 | 7f37d5ec7000-7f37d5ec8000 rw-p 00001000 ca:01 267321 /home/ubuntu/linux-inject/sample-library.so 80 | [...] 81 | 82 | * You can also attach `gdb` to the target app and run `info sharedlibrary` to see what shared libraries the process currently has loaded: 83 | 84 | $ gdb -p $(pgrep sample-target) 85 | [...] 86 | (gdb) info sharedlibrary 87 | From To Syms Read Shared Object Library 88 | 0x00007f37d628ded0 0x00007f37d628e9ce Yes /lib/x86_64-linux-gnu/libdl.so.2 89 | 0x00007f37d5ee74a0 0x00007f37d602c583 Yes /lib/x86_64-linux-gnu/libc.so.6 90 | 0x00007f37d6491ae0 0x00007f37d64ac4e0 Yes /lib64/ld-linux-x86-64.so.2 91 | 0x00007f37d5cc6670 0x00007f37d5cc67b9 Yes /home/ubuntu/linux-inject/sample-library.so 92 | (gdb) 93 | 94 | ## Compatibility 95 | 96 | * The x86 and x86_64 versions work on Ubuntu 14.04.02 x86_64. 97 | 98 | * The x86 and x86_64 versions work on Arch x86_64. 99 | 100 | * The ARM version works on Arch on both armv6 and armv7. 101 | 102 | * None of the versions seem to work on Debian. `__libc_dlopen_mode()` in Debian's libc does not load shared libraries in the same manner as Arch's and Ubuntu's versions do. I tested this on both x86_64 and armv6. 103 | 104 | ## TODOs / Known Issues 105 | 106 | * Better support for targeting multi-thread/multi-process apps 107 | * I seem to get crashes when trying to inject into larger applications 108 | * Needs further investigation 109 | 110 | * Support both ARM and Thumb mode 111 | * Currently only supports ARM mode 112 | * Should just be a matter of checking LSB of PC and acting accordingly 113 | 114 | * Do better checking to verify that the specified shared object has actually been injected into the target process 115 | * Check `/proc/[pid]/maps` rather than just looking at the return value of `__libc_dlopen_mode()` 116 | 117 | * Support more distros 118 | * Currently only working on Ubuntu and Arch for certain architectures 119 | * See "Compatibility" section above 120 | 121 | * Possibly support more architectures? 122 | * 64-bit ARM 123 | * MIPS 124 | -------------------------------------------------------------------------------- /patches/CVE-2015-0235/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # upatch specifics 3 | # 4 | # based on: 5 | # glibc-2.17-157.el7.src.rpm 6 | 7 | UPATCH=cve_2015_0235 8 | TRAMPOLINES := \ 9 | /usr/lib64/libc.so.6 __nss_hostname_digits_dots 10 | 11 | PATCH_SRC=patches.c 12 | 13 | patches.o: patches.c 14 | $(CC) \ 15 | $< \ 16 | -fPIC \ 17 | -c \ 18 | -std=gnu99 \ 19 | -fgnu89-inline \ 20 | -DNDEBUG \ 21 | -O3 \ 22 | -Wall \ 23 | -Winline \ 24 | -Wwrite-strings \ 25 | -fasynchronous-unwind-tables \ 26 | -fmerge-all-constants \ 27 | -frounding-math \ 28 | -g \ 29 | -mtune=generic \ 30 | -Wstrict-prototypes \ 31 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/include \ 32 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/build-x86_64-redhat-linux/nss \ 33 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/build-x86_64-redhat-linux \ 34 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv/linux/x86_64/64/nptl \ 35 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv/linux/x86_64/64 \ 36 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/unix/sysv/linux/x86_64 \ 37 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/unix/sysv/linux/x86 \ 38 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv/linux/x86 \ 39 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/rtkaio/sysdeps/unix/sysv/linux/x86_64 \ 40 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv/linux/x86_64 \ 41 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv/linux/wordsize-64 \ 42 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/ports/sysdeps/unix/sysv/linux \ 43 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/unix/sysv/linux \ 44 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/pthread \ 45 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/rtkaio/sysdeps/pthread \ 46 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/pthread \ 47 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/rtkaio/sysdeps/unix/sysv/linux \ 48 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv/linux \ 49 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/gnu \ 50 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/inet \ 51 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/ports/sysdeps/unix/sysv \ 52 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/unix/sysv \ 53 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/rtkaio/sysdeps/unix/sysv \ 54 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/sysv \ 55 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix/x86_64 \ 56 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/ports/sysdeps/unix \ 57 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/unix \ 58 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/rtkaio/sysdeps/unix \ 59 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/unix \ 60 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/posix \ 61 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/x86_64/64 \ 62 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86_64/64 \ 63 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86_64/fpu/multiarch \ 64 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86_64/fpu \ 65 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86/fpu \ 66 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86_64/multiarch \ 67 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl/sysdeps/x86_64 \ 68 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86_64 \ 69 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/x86 \ 70 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/ieee754/ldbl-96 \ 71 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/ieee754/dbl-64/wordsize-64 \ 72 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/ieee754/dbl-64 \ 73 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/ieee754/flt-32 \ 74 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/wordsize-64 \ 75 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/ieee754 \ 76 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/sysdeps/generic \ 77 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/ports \ 78 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nptl \ 79 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/rtkaio \ 80 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686 \ 81 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/libio \ 82 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/nss \ 83 | -nostdinc \ 84 | -isystem \ 85 | /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include \ 86 | -isystem \ 87 | /usr/include \ 88 | -D_LIBC_REENTRANT \ 89 | -include \ 90 | /root/rpmbuild/BUILD/glibc-2.17-c758a686/build-x86_64-redhat-linux/libc-modules.h \ 91 | -include \ 92 | /root/rpmbuild/BUILD/glibc-2.17-c758a686/include/libc-symbols.h \ 93 | -o \ 94 | $@ 95 | 96 | 97 | # 98 | # upatch boilerplate 99 | # 100 | 101 | .DEFAULT_GOAL:=all 102 | all: libupatch_$(UPATCH).so 103 | 104 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 105 | 106 | libupatch_$(UPATCH).so: $(PATCH_SRC:.c=.o) trampolines.o 107 | $(CC) -shared -lupatch -lsystemd -Wl,-soname,$@ -o $@ $^ 108 | 109 | -include trampolines.d 110 | 111 | trampolines.c: 112 | ../gen_trampolines ${TRAMPOLINES} > $@ 113 | 114 | install: all 115 | install -D libupatch_$(UPATCH).so /usr/lib64 116 | ldconfig -n /usr/lib64 117 | 118 | uninstall: 119 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 120 | ldconfig -n /usr/lib64 121 | 122 | clean: 123 | $(RM) -f trampolines.c *.d *.o *.so 124 | 125 | .PHONY: install uninstall clean 126 | 127 | -------------------------------------------------------------------------------- /patches/README.md: -------------------------------------------------------------------------------- 1 | # patches 2 | 3 | **!!! Note !!!** 4 | This file describes the state of this project on Sep 13th. See this [commit](https://github.com/joe-lawrence/linux-inject/tree/d5dcc43f2221320c5a307e25ba7b5a053b1fcfc4/patches) for reference. 5 | 6 | 7 | ## Proof-of-concept tool for patching a running process 8 | The patches/ subdirectory provides a shared library "patch" for use with [linux-inject](https://github.com/gaffe23/linux-inject) project. Code inside the shared library patch is intended to replace broken or vulnerable code inside a running process with out restart. 9 | 10 | ## How it works 11 | 12 | ### patches.c 13 | 14 | A patches.c file includes new code ```patched_lib_private``` and ```patched_pgm_function``` (not shown here) but referenced in a global ```trampolines``` structure. Many of these fields (```.map_name```, ```.offset```, ```.type```) are only required as part of the proof-of-concept. (This is understandably a bit clunky to hardcode into the shared library patch, but they simplify the POC by avoding ELF or DWARF header parsing.): 15 | 16 | struct trampolines tramps[] = { 17 | 18 | { .oldname = "lib_private", 19 | .new_addr = patched_lib_private, 20 | .map_name = "/tmp/libtest.so", 21 | .offset = 0x0000000000000758, /* nm test/libtest.so | grep lib_private */ 22 | .type = T_OFFSET_REL, }, 23 | 24 | { .oldname = "pgm_function", 25 | .new_addr = patched_pgm_function, 26 | .map_name = "/tmp/test", 27 | .offset = 0x00000000004006e0, /* nm test/test | grep pgm_function */ 28 | .type = T_OFFSET_ABS, }, 29 | 30 | { 0 } }; 31 | 32 | ### linux-inject 33 | 34 | The shared library patch is injected into the test program using the [linux-inject](https://github.com/gaffe23/linux-inject). When the shared library patch is loaded, its constructor uses ```trampoline[]``` data to save the first 16 bytes of a given entry point and replaces it with a code "trampoline" to the new one. 35 | 36 | For example, before [linux-inject](https://github.com/gaffe23/linux-inject): 37 | 38 | (gdb) disassemble pgm_function 39 | Dump of assembler code for function pgm_function: 40 | 0x0000000000400780 <+0>: sub $0x18,%rsp 41 | 0x0000000000400784 <+4>: mov %fs:0x28,%rax 42 | 0x000000000040078d <+13>: mov %rax,0x8(%rsp) 43 | 0x0000000000400792 <+18>: xor %eax,%eax 44 | 0x0000000000400794 <+20>: lea 0xf2(%rip),%rdx # 0x40088d <__func__.2386> 45 | 0x000000000040079b <+27>: lea 0xde(%rip),%rsi # 0x400880 46 | 0x00000000004007a2 <+34>: mov $0x1,%edi 47 | 0x00000000004007a7 <+39>: callq 0x400680 <__printf_chk@plt> 48 | 0x00000000004007ac <+44>: mov 0x8(%rsp),%rax 49 | 0x00000000004007b1 <+49>: xor %fs:0x28,%rax 50 | 0x00000000004007ba <+58>: je 0x4007c1 51 | 0x00000000004007bc <+60>: callq 0x400640 <__stack_chk_fail@plt> 52 | 0x00000000004007c1 <+65>: add $0x18,%rsp 53 | 0x00000000004007c5 <+69>: retq 54 | End of assembler dump. 55 | 56 | After [linux-inject](https://github.com/gaffe23/linux-inject) is executed, the process's ```/proc/PID/maps``` file is updated to reflect the new shared library mappings: 57 | 58 | /proc//maps 59 | 00400000-00401000 r-xp 00000000 fd:00 17609433 /tmp/linux-inject/patches/test/test 60 | 00600000-00601000 r--p 00000000 fd:00 17609433 /tmp/linux-inject/patches/test/test 61 | 00601000-00602000 rw-p 00001000 fd:00 17609433 /tmp/linux-inject/patches/test/test 62 | ... 63 | 7f12bbbd5000-7f12bbbd7000 r-xp 00000000 fd:00 73423 /tmp/linux-inject/patches/libupatch.so 64 | 7f12bbbd7000-7f12bbdd6000 ---p 00002000 fd:00 73423 /tmp/linux-inject/patches/libupatch.so 65 | 7f12bbdd6000-7f12bbdd7000 r--p 00001000 fd:00 73423 /tmp/linux-inject/patches/libupatch.so 66 | 7f12bbdd7000-7f12bbdd8000 rw-p 00002000 fd:00 73423 /tmp/linux-inject/patches/libupatch.so 67 | 68 | ### Code trampolines 69 | 70 | After ```linux-inject``` is executed, code trampolines are set in place, patching old functions with the shared library patch: 71 | 72 | (gdb) disassemble pgm_function 73 | Dump of assembler code for function pgm_function: 74 | 0x0000000000400780 <+0>: pushq $0xffffffffbbbd6307 << trampoline 75 | 0x0000000000400785 <+5>: movl $0x7f12,0x4(%rsp) << to new 76 | 0x000000000040078d <+13>: retq << code 77 | 0x000000000040078e <+14>: add %al,(%rax) 78 | 0x0000000000400790 <+16>: and $0x8,%al 79 | 0x0000000000400792 <+18>: xor %eax,%eax 80 | 0x0000000000400794 <+20>: lea 0xf2(%rip),%rdx # 0x40088d <__func__.2386> 81 | 0x000000000040079b <+27>: lea 0xde(%rip),%rsi # 0x400880 82 | 0x00000000004007a2 <+34>: mov $0x1,%edi 83 | 0x00000000004007a7 <+39>: callq 0x400680 <__printf_chk@plt> 84 | 0x00000000004007ac <+44>: mov 0x8(%rsp),%rax 85 | 0x00000000004007b1 <+49>: xor %fs:0x28,%rax 86 | 0x00000000004007ba <+58>: je 0x4007c1 87 | 0x00000000004007bc <+60>: callq 0x400640 <__stack_chk_fail@plt> 88 | 0x00000000004007c1 <+65>: add $0x18,%rsp 89 | 0x00000000004007c5 <+69>: retq 90 | End of assembler dump. 91 | 92 | The trampoline code is fairly simple, but relies on a trick documented on Nikolay Igotti's blog in [Long absolute jumps on AMD64](https://blogs.oracle.com/nike/entry/long_absolute_jumps_on_amd64). All told, the trampoline amounts to only 16 bytes that we need to manage. 93 | 94 | ## POC Shortcomings 95 | 96 | This proof-of-concept inhereits any limitations of the [linux-inject](https://github.com/gaffe23/linux-inject) project as well as a few others: 97 | 98 | 1. The git repo should be cloned and run out of ```/tmp``` 99 | 2. ```constructor.c``` has a bunch of magic number and other data that could be determined at runtime 100 | 3. Target functions (i.e., those to patch) must be at least 16 bytes (the size of the code trampoline) long 101 | 4. During patching operation, target process cannot be running as a gdb, strace or any other ptrace client 102 | 5. [linux-inject](https://github.com/gaffe23/linux-inject) supports x86, x86_64, and ARM, but patches only implements x86_64 code 103 | 99. Many more, I'm sure 104 | 105 | ## Demo 106 | 107 | *Bonus*: Because ```linux-inject``` utilizes the dynamic linker, gdb recognizes the shared library patch symbols: 108 | 109 | (gdb) info symbol 0x7f12bbbd6307 110 | patched_pgm_function in section .text of /tmp/linux-inject/patches/libupatch.so 111 | 112 | ![Demo](demo.gif) 113 | -------------------------------------------------------------------------------- /patches/ideas/fentry/fentry_override.md: -------------------------------------------------------------------------------- 1 | # Patching functions via __fentry__ override 2 | 3 | ## Compiler options 4 | 5 | Consider a simple C function: 6 | 7 | int my_function(int a) 8 | { 9 | printf("my function: %d!\n", a); 10 | } 11 | 12 | Normal compilation looks like this: 13 | 14 | 0000000000400591 : 15 | 400591: 55 push %rbp 16 | 400592: 48 89 e5 mov %rsp,%rbp 17 | 400595: 48 83 ec 10 sub $0x10,%rsp 18 | 400599: 89 7d fc mov %edi,-0x4(%rbp) 19 | 40059c: 8b 45 fc mov -0x4(%rbp),%eax 20 | 40059f: 89 c6 mov %eax,%esi 21 | 4005a1: bf 7e 06 40 00 mov $0x40067e,%edi 22 | 4005a6: b8 00 00 00 00 mov $0x0,%eax 23 | 4005ab: e8 a0 fe ff ff callq 400450 24 | 4005b0: c9 leaveq 25 | 4005b1: c3 retq 26 | 27 | With the ```-pg``` option to add extra code for writing profiling 28 | information (additional instructions prefixed with '>'). Adds 5 bytes 29 | to the function size: 30 | 31 | 00000000004005e6 : 32 | 4005e6: 55 push %rbp 33 | 4005e7: 48 89 e5 mov %rsp,%rbp 34 | 4005ea: 48 83 ec 10 sub $0x10,%rsp 35 | > 4005ee: e8 cd fe ff ff callq 4004c0 36 | 4005f3: 89 7d fc mov %edi,-0x4(%rbp) 37 | 4005f6: 8b 45 fc mov -0x4(%rbp),%eax 38 | 4005f9: 89 c6 mov %eax,%esi 39 | 4005fb: bf de 06 40 00 mov $0x4006de,%edi 40 | 400600: b8 00 00 00 00 mov $0x0,%eax 41 | 400605: e8 86 fe ff ff callq 400490 42 | 40060a: c9 leaveq 43 | 40060b: c3 retq 44 | 45 | With ```-pg -mfentry``` options to put the profiling counter call 46 | *before* the prologue. Adds 5 bytes to function size: 47 | 48 | 00000000004005c6 : 49 | > 4005c6: e8 d5 fe ff ff callq 4004a0 <__fentry__@plt> 50 | 4005cb: 55 push %rbp 51 | 4005cc: 48 89 e5 mov %rsp,%rbp 52 | 4005cf: 48 83 ec 10 sub $0x10,%rsp 53 | 4005d3: 89 7d fc mov %edi,-0x4(%rbp) 54 | 4005d6: 8b 45 fc mov -0x4(%rbp),%eax 55 | 4005d9: 89 c6 mov %eax,%esi 56 | 4005db: bf ae 06 40 00 mov $0x4006ae,%edi 57 | 4005e0: b8 00 00 00 00 mov $0x0,%eax 58 | 4005e5: e8 86 fe ff ff callq 400470 59 | 4005ea: c9 leaveq 60 | 4005eb: c3 retq 61 | 62 | With ```-pg -finstrument-functions``` options, which will generate 63 | instrumentation calls for entry and exit to functions. Adds 34 bytes to 64 | the function size: 65 | 66 | 0000000000400613 : 67 | 400613: 55 push %rbp 68 | 400614: 48 89 e5 mov %rsp,%rbp 69 | 400617: 48 83 ec 10 sub $0x10,%rsp 70 | 40061b: 89 7d fc mov %edi,-0x4(%rbp) 71 | > 40061e: 48 8b 45 08 mov 0x8(%rbp),%rax 72 | > 400622: 48 89 c6 mov %rax,%rsi 73 | > 400625: bf 13 06 40 00 mov $0x400613,%edi 74 | > 40062a: e8 27 00 00 00 callq 400656 <__cyg_profile_func_enter> 75 | 40062f: 8b 45 fc mov -0x4(%rbp),%eax 76 | 400632: 89 c6 mov %eax,%esi 77 | 400634: bf 4e 07 40 00 mov $0x40074e,%edi 78 | 400639: b8 00 00 00 00 mov $0x0,%eax 79 | 40063e: e8 5d fe ff ff callq 4004a0 80 | > 400643: 48 8b 45 08 mov 0x8(%rbp),%rax 81 | > 400647: 48 89 c6 mov %rax,%rsi 82 | > 40064a: bf 13 06 40 00 mov $0x400613,%edi 83 | > 40064f: e8 7c fe ff ff callq 4004d0 <__cyg_profile_func_exit@plt> 84 | 400654: c9 leaveq 85 | 400655: c3 retq 86 | 87 | ## The experiment 88 | 89 | For purposes of this experiment, we'll be using the ```-pg --mfentry``` 90 | options. It minimally affects the function code and stack. This also 91 | what the kernel's ftrace facility currently uses. 92 | 93 | Compile the .c file with ```-pg --mfentry``` so the compiler inserts 94 | calls to ```__fentry___``` at the beginning of every function. Omit 95 | these flags when linking the final executable to avoid generating the 96 | ```gmon.out``` profiling data file. 97 | 98 | % gcc fentry_override.c -pg -mfentry -c -o fentry_override.o 99 | % gcc -o fentry_override fentry_override.o 100 | 101 | % objdump -DS /tmp/fentry_override 102 | ... 103 | 104 | 00000000004005c6 : 105 | 106 | int my_function(int a) 107 | { 108 | 4005c6: e8 d5 fe ff ff callq 4004a0 <__fentry__@plt> 109 | ... 110 | 111 | Now that every function call is routed through ```__fentry__```, we can 112 | provide our own implementation to patches the return address ... sending 113 | the caller off to a patched function and not the original function: 114 | 115 | /* fentry_override.c */ 116 | #include 117 | #include 118 | 119 | #define FENTRY_BYTES (5) 120 | 121 | int my_patch(int a) 122 | { 123 | printf("my_patch: %d\n", a); 124 | } 125 | 126 | int my_function(int a) 127 | { 128 | printf("my function: %d!\n", a); 129 | } 130 | 131 | __attribute__((no_instrument_function)) 132 | void __fentry__(void) 133 | { 134 | uint64_t returnaddr; 135 | 136 | asm("mov 8(%%rbp),%0" : "=r"(returnaddr) : : ); 137 | 138 | if (returnaddr == (uint64_t) my_function + FENTRY_BYTES) { 139 | returnaddr = (uint64_t) my_patch + FENTRY_BYTES; 140 | asm("mov %0,8(%%rbp)" : "=r"(returnaddr) : : ); 141 | } 142 | } 143 | 144 | int main() 145 | { 146 | my_function(123); 147 | 148 | return 0; 149 | } 150 | 151 | % ./fentry_override 152 | my_patch: 123 153 | 154 | ## Additional thoughts 155 | 156 | ```__fentry__``` could be provided in a shared library: 157 | 158 | 1. Target build is modified: ```-l``` 159 | 2. ```LD_PRELOAD``` when starting target 160 | 3. Injected via ```ptrace``` + call to ```dlopen``` 161 | 162 | The next hurdle becomes patch management, i.e. how would our 163 | ```__fentry__``` implementation know which functions to patch and where 164 | to go? Maybe something like the following: 165 | 166 | 1. ```__fentry__``` will look at some global variable, if it is set, 167 | then it will ```dlopen``` a shared library patch. 168 | 2. The shared library patch will write to a global ```patches``` array, 169 | providing some reference to the "old" function and the "new" function. 170 | 3. ```__fentry__``` continues execution, running through the global 171 | ```patch``` array testing to see if the current caller should be 172 | patched. 173 | 174 | ## Links 175 | 176 | [Ftrace Kernel Hooks:More than just 177 | tracing](https://linuxplumbersconf.org/2014/ocw/system/presentations/1773/original/ftrace-kernel-hooks-2014.pdf) - slides on kernel ftrace implementation 178 | 179 | [lkml: Re: RFC PATCH Make ftrace able to trace function return](https://lkml.org/lkml/2008/10/30/372) - mailing list conversation about compiler options 180 | 181 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | 10 | /* 11 | * findProcessByName() 12 | * 13 | * Given the name of a process, try to find its PID by searching through /proc 14 | * and reading /proc/[pid]/exe until we find a process whose name matches the 15 | * given process. 16 | * 17 | * args: 18 | * - char* processName: name of the process whose pid to find 19 | * 20 | * returns: 21 | * - a pid_t containing the pid of the process (or -1 if not found) 22 | * 23 | */ 24 | 25 | pid_t findProcessByName(char* processName) 26 | { 27 | if(processName == NULL) 28 | { 29 | return -1; 30 | } 31 | 32 | struct dirent *procDirs; 33 | 34 | DIR *directory = opendir("/proc/"); 35 | 36 | if (directory) 37 | { 38 | while ((procDirs = readdir(directory)) != NULL) 39 | { 40 | if (procDirs->d_type != DT_DIR) 41 | continue; 42 | 43 | pid_t pid = atoi(procDirs->d_name); 44 | 45 | int exePathLen = 10 + strlen(procDirs->d_name) + 1; 46 | char* exePath = malloc(exePathLen * sizeof(char)); 47 | 48 | if(exePath == NULL) 49 | { 50 | continue; 51 | } 52 | 53 | sprintf(exePath, "/proc/%s/exe", procDirs->d_name); 54 | exePath[exePathLen-1] = '\0'; 55 | 56 | char* exeBuf = malloc(PATH_MAX * sizeof(char)); 57 | if(exeBuf == NULL) 58 | { 59 | free(exePath); 60 | continue; 61 | } 62 | ssize_t len = readlink(exePath, exeBuf, PATH_MAX - 1); 63 | 64 | if(len == -1) 65 | { 66 | free(exePath); 67 | free(exeBuf); 68 | continue; 69 | } 70 | 71 | exeBuf[len] = '\0'; 72 | 73 | char* exeName = NULL; 74 | char* exeToken = strtok(exeBuf, "/"); 75 | while(exeToken) 76 | { 77 | exeName = exeToken; 78 | exeToken = strtok(NULL, "/"); 79 | } 80 | 81 | if(strcmp(exeName, processName) == 0) 82 | { 83 | free(exePath); 84 | free(exeBuf); 85 | closedir(directory); 86 | return pid; 87 | } 88 | 89 | free(exePath); 90 | free(exeBuf); 91 | } 92 | 93 | closedir(directory); 94 | } 95 | 96 | return -1; 97 | } 98 | 99 | /* 100 | * freespaceaddr() 101 | * 102 | * Search the target process' /proc/pid/maps entry and find an executable 103 | * region of memory that we can use to run code in. 104 | * 105 | * args: 106 | * - pid_t pid: pid of process to inspect 107 | * 108 | * returns: 109 | * - a long containing the address of an executable region of memory inside the 110 | * specified process' address space. 111 | * 112 | */ 113 | 114 | long freespaceaddr(pid_t pid) 115 | { 116 | FILE *fp; 117 | char filename[30]; 118 | char line[850]; 119 | long addr; 120 | char str[20]; 121 | char perms[5]; 122 | sprintf(filename, "/proc/%d/maps", pid); 123 | fp = fopen(filename, "r"); 124 | if(fp == NULL) 125 | exit(1); 126 | while(fgets(line, 850, fp) != NULL) 127 | { 128 | sscanf(line, "%lx-%*lx %s %*s %s %*d", &addr, perms, str); 129 | 130 | if(strstr(perms, "x") != NULL) 131 | { 132 | break; 133 | } 134 | } 135 | fclose(fp); 136 | return addr; 137 | } 138 | 139 | /* 140 | * getlibcaddr() 141 | * 142 | * Gets the base address of libc.so inside a process by reading /proc/pid/maps. 143 | * 144 | * args: 145 | * - pid_t pid: the pid of the process whose libc.so base address we should 146 | * find 147 | * 148 | * returns: 149 | * - a long containing the base address of libc.so inside that process 150 | * 151 | */ 152 | 153 | long getlibcaddr(pid_t pid) 154 | { 155 | FILE *fp; 156 | char filename[30]; 157 | char line[850]; 158 | long addr; 159 | char perms[5]; 160 | char* modulePath; 161 | sprintf(filename, "/proc/%d/maps", pid); 162 | fp = fopen(filename, "r"); 163 | if(fp == NULL) 164 | exit(1); 165 | while(fgets(line, 850, fp) != NULL) 166 | { 167 | sscanf(line, "%lx-%*lx %*s %*s %*s %*d", &addr); 168 | if(strstr(line, "libc-") != NULL) 169 | { 170 | break; 171 | } 172 | } 173 | fclose(fp); 174 | return addr; 175 | } 176 | 177 | /* 178 | * checkloaded() 179 | * 180 | * Given a process ID and the name of a shared library, check whether that 181 | * process has loaded the shared library by reading entries in its 182 | * /proc/[pid]/maps file. 183 | * 184 | * args: 185 | * - pid_t pid: the pid of the process to check 186 | * - char* libname: the library to search /proc/[pid]/maps for 187 | * 188 | * returns: 189 | * - an int indicating whether or not the library has been loaded into the 190 | * process (1 = yes, 0 = no) 191 | * 192 | */ 193 | 194 | int checkloaded(pid_t pid, char* libname) 195 | { 196 | FILE *fp; 197 | char filename[30]; 198 | char line[850]; 199 | long addr; 200 | char perms[5]; 201 | char* modulePath; 202 | sprintf(filename, "/proc/%d/maps", pid); 203 | fp = fopen(filename, "r"); 204 | if(fp == NULL) 205 | exit(1); 206 | while(fgets(line, 850, fp) != NULL) 207 | { 208 | sscanf(line, "%lx-%*lx %*s %*s %*s %*d", &addr); 209 | if(strstr(line, libname) != NULL) 210 | { 211 | fclose(fp); 212 | return 1; 213 | } 214 | } 215 | fclose(fp); 216 | return 0; 217 | } 218 | 219 | /* 220 | * getFunctionAddress() 221 | * 222 | * Find the address of a function within our own loaded copy of libc.so. 223 | * 224 | * args: 225 | * - char* funcName: name of the function whose address we want to find 226 | * 227 | * returns: 228 | * - a long containing the address of that function 229 | * 230 | */ 231 | 232 | long getFunctionAddress(char* funcName) 233 | { 234 | void* self = dlopen("libc.so.6", RTLD_LAZY); 235 | void* funcAddr = dlsym(self, funcName); 236 | return (long)funcAddr; 237 | } 238 | 239 | /* 240 | * findRet() 241 | * 242 | * Starting at an address somewhere after the end of a function, search for the 243 | * "ret" instruction that ends it. We do this by searching for a 0xc3 byte, and 244 | * assuming that it represents that function's "ret" instruction. This should 245 | * be a safe assumption. Function addresses are word-aligned, and so there's 246 | * usually extra space at the end of a function. This space is always padded 247 | * with "nop"s, so we'll end up just searching through a series of "nop"s 248 | * before finding our "ret". In other words, it's unlikely that we'll run into 249 | * a 0xc3 byte that corresponds to anything other than an actual "ret" 250 | * instruction. 251 | * 252 | * Note that this function only applies to x86 and x86_64, and not ARM. 253 | * 254 | * args: 255 | * - void* endAddr: the ending address of the function whose final "ret" 256 | * instruction we want to find 257 | * 258 | * returns: 259 | * - an unsigned char* pointing to the address of the final "ret" instruction 260 | * of the specified function 261 | * 262 | */ 263 | 264 | unsigned char* findRet(void* endAddr) 265 | { 266 | unsigned char* retInstAddr = endAddr; 267 | while(*retInstAddr != INTEL_RET_INSTRUCTION) 268 | { 269 | retInstAddr--; 270 | } 271 | return retInstAddr; 272 | } 273 | 274 | /* 275 | * usage() 276 | * 277 | * Print program usage and exit. 278 | * 279 | * args: 280 | * - char* name: the name of the executable we're running out of 281 | * 282 | */ 283 | 284 | void usage(char* name) 285 | { 286 | printf("usage: %s [-n process-name] [-p pid] [library-to-inject]\n", name); 287 | } 288 | -------------------------------------------------------------------------------- /unject-x86_64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | #include "ptrace.h" 10 | 11 | /* 12 | * injectSharedLibrary() 13 | * 14 | * This is the code that will actually be injected into the target process. 15 | * This code is responsible for loading the shared library into the target 16 | * process' address space. First, it calls malloc() to allocate a buffer to 17 | * hold the filename of the library to be loaded. Then, it calls 18 | * __libc_dlopen_mode(), libc's implementation of dlopen(), to load the desired 19 | * shared library. Finally, it calls free() to free the buffer containing the 20 | * library name. Each time it needs to give control back to the injector 21 | * process, it breaks back in by executing an "int $3" instruction. See the 22 | * comments below for more details on how this works. 23 | * 24 | */ 25 | 26 | void injectSharedLibrary(long handle, long dlcloseaddr) 27 | { 28 | // here are the assumptions I'm making about what data will be located 29 | // where at the time the target executes this code: 30 | // 31 | // rdi = (previously injected) dynamic library handle 32 | // rsi = address of __libc_dlclose() in target process 33 | 34 | asm( 35 | "callq %rsi \n" 36 | ); 37 | 38 | // we already overwrote the RET instruction at the end of this function 39 | // with an INT 3, so at this point the injector will regain control of 40 | // the target's execution. 41 | } 42 | 43 | /* 44 | * injectSharedLibrary_end() 45 | * 46 | * This function's only purpose is to be contiguous to injectSharedLibrary(), 47 | * so that we can use its address to more precisely figure out how long 48 | * injectSharedLibrary() is. 49 | * 50 | */ 51 | 52 | void injectSharedLibrary_end() 53 | { 54 | } 55 | 56 | int main(int argc, char** argv) 57 | { 58 | if(argc < 4) 59 | { 60 | usage(argv[0]); 61 | return 1; 62 | } 63 | 64 | char* command = argv[1]; 65 | char* commandArg = argv[2]; 66 | 67 | char* strhandle = argv[3]; 68 | long handle = strtol(strhandle, NULL, 16); 69 | 70 | char* processName = NULL; 71 | pid_t target = 0; 72 | 73 | if(!handle) 74 | { 75 | fprintf(stderr, "\tno handle!\n"); 76 | return 1; 77 | } 78 | 79 | if(!strcmp(command, "-n")) 80 | { 81 | processName = commandArg; 82 | target = findProcessByName(processName); 83 | if(target == -1) 84 | { 85 | fprintf(stderr, "\tdoesn't look like a process named \"%s\" is running right now\n", processName); 86 | return 1; 87 | } 88 | 89 | printf("\ttargeting process \"%s\" with pid %d\n", processName, target); 90 | } 91 | else if(!strcmp(command, "-p")) 92 | { 93 | target = atoi(commandArg); 94 | printf("\ttargeting process with pid %d\n", target); 95 | } 96 | else 97 | { 98 | usage(argv[0]); 99 | return 1; 100 | } 101 | 102 | int mypid = getpid(); 103 | long mylibcaddr = getlibcaddr(mypid); 104 | 105 | // find the addresses of the syscalls that we'd like to use inside the 106 | // target, as loaded inside THIS process (i.e. NOT the target process) 107 | long dlcloseAddr = getFunctionAddress("__libc_dlclose"); 108 | 109 | // use the base address of libc to calculate offsets for the syscalls 110 | // we want to use 111 | long dlcloseOffset = dlcloseAddr - mylibcaddr; 112 | 113 | 114 | // get the target process' libc address and use it to find the 115 | // addresses of the syscalls we want to use inside the target process 116 | long targetLibcAddr = getlibcaddr(target); 117 | long targetDlcloseAddr = targetLibcAddr + dlcloseOffset; 118 | 119 | struct user_regs_struct oldregs, regs; 120 | memset(&oldregs, 0, sizeof(struct user_regs_struct)); 121 | memset(®s, 0, sizeof(struct user_regs_struct)); 122 | 123 | ptrace_attach(target); 124 | 125 | ptrace_getregs(target, &oldregs); 126 | memcpy(®s, &oldregs, sizeof(struct user_regs_struct)); 127 | 128 | // find a good address to copy code to 129 | long addr = freespaceaddr(target) + sizeof(long); 130 | 131 | // now that we have an address to copy code to, set the target's rip to 132 | // it. we have to advance by 2 bytes here because rip gets incremented 133 | // by the size of the current instruction, and the instruction at the 134 | // start of the function to inject always happens to be 2 bytes long. 135 | regs.rip = addr + 2; 136 | 137 | // pass arguments to my function injectSharedLibrary() by loading them 138 | // into the right registers. note that this will definitely only work 139 | // on x64, because it relies on the x64 calling convention, in which 140 | // arguments are passed via registers rdi, rsi, rdx, rcx, r8, and r9. 141 | // see comments in injectSharedLibrary() for more details. 142 | regs.rdi = handle; 143 | regs.rsi = targetDlcloseAddr; 144 | ptrace_setregs(target, ®s); 145 | 146 | // figure out the size of injectSharedLibrary() so we know how big of a buffer to allocate. 147 | size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)injectSharedLibrary; 148 | 149 | // also figure out where the RET instruction at the end of 150 | // injectSharedLibrary() lies so that we can overwrite it with an INT 3 151 | // in order to break back into the target process. note that on x64, 152 | // gcc and clang both force function addresses to be word-aligned, 153 | // which means that functions are padded with NOPs. as a result, even 154 | // though we've found the length of the function, it is very likely 155 | // padded with NOPs, so we need to actually search to find the RET. 156 | intptr_t injectSharedLibrary_ret = (intptr_t)findRet(injectSharedLibrary_end) - (intptr_t)injectSharedLibrary; 157 | 158 | // back up whatever data used to be at the address we want to modify. 159 | char* backup = malloc(injectSharedLibrary_size * sizeof(char)); 160 | ptrace_read(target, addr, backup, injectSharedLibrary_size); 161 | 162 | // set up a buffer to hold the code we're going to inject into the 163 | // target process. 164 | char* newcode = malloc(injectSharedLibrary_size * sizeof(char)); 165 | memset(newcode, 0, injectSharedLibrary_size * sizeof(char)); 166 | 167 | // copy the code of injectSharedLibrary() to a buffer. 168 | memcpy(newcode, injectSharedLibrary, injectSharedLibrary_size - 1); 169 | // overwrite the RET instruction with an INT 3. 170 | newcode[injectSharedLibrary_ret] = INTEL_INT3_INSTRUCTION; 171 | 172 | // copy injectSharedLibrary()'s code to the target address inside the 173 | // target process' address space. 174 | ptrace_write(target, addr, newcode, injectSharedLibrary_size); 175 | 176 | // now that the new code is in place, let the target run our injected 177 | // code. 178 | ptrace_cont(target); 179 | 180 | // check out what the registers look like after calling dlopen. 181 | struct user_regs_struct dlopen_regs; 182 | memset(&dlopen_regs, 0, sizeof(struct user_regs_struct)); 183 | ptrace_getregs(target, &dlopen_regs); 184 | unsigned long long ret = dlopen_regs.rax; 185 | 186 | // if rax is 0 here, then __libc_dlclose_mode failed, and we should bail 187 | // out cleanly. 188 | if(ret == 0) 189 | { 190 | printf("\t__libc_dlclose() unloaded handle 0x%x\n", handle); 191 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 192 | free(backup); 193 | free(newcode); 194 | return 1; 195 | } 196 | 197 | // at this point, if everything went according to plan, we've loaded 198 | // the shared library inside the target process, so we're done. restore 199 | // the old state and detach from the target. 200 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 201 | free(backup); 202 | free(newcode); 203 | 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /patches/upatch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define UNW_LOCAL_ONLY 12 | #include 13 | #include "upatch.h" 14 | 15 | 16 | /* TODO: replace with reading self? */ 17 | /* From linux kernel: tools/vm/page-types.c */ 18 | static unsigned long get_dso_base(const char *path, const char *sym) 19 | { 20 | struct stat st; 21 | FILE *file; 22 | char buf[5000]; 23 | unsigned long last_vm_start = -1; 24 | 25 | lstat(path, &st); 26 | 27 | sprintf(buf, "/proc/%d/maps", getpid()); 28 | file = fopen(buf, "r"); 29 | if (!file) { 30 | perror(buf); 31 | exit(EXIT_FAILURE); 32 | } 33 | 34 | while (fgets(buf, sizeof(buf), file) != NULL) { 35 | unsigned long vm_start; 36 | unsigned long vm_end; 37 | unsigned long long pgoff; 38 | int major, minor; 39 | char r, w, x, s; 40 | unsigned long ino; 41 | int n; 42 | 43 | n = sscanf(buf, "%lx-%lx %c%c%c%c %llx %x:%x %lu", 44 | &vm_start, 45 | &vm_end, 46 | &r, &w, &x, &s, 47 | &pgoff, 48 | &major, &minor, 49 | &ino); 50 | if (n < 10) { 51 | fprintf(stderr, "unexpected line: %s\n", buf); 52 | continue; 53 | } 54 | 55 | if ((st.st_ino == ino) && 56 | (r == 'r' && w == '-' && x == 'x' && s == 'p')) { 57 | last_vm_start = vm_start; 58 | } 59 | } 60 | fclose(file); 61 | 62 | return last_vm_start; 63 | } 64 | 65 | 66 | /* Read ELF note containing build id, 67 | * from: http://stackoverflow.com/questions/17637745/can-a-program-read-its-own-elf-section 68 | * TODO: can libelf handle this? */ 69 | 70 | #if __x86_64__ 71 | # define ElfW(type) Elf64_##type 72 | #else 73 | # define ElfW(type) Elf32_##type 74 | #endif 75 | 76 | static int build_id_cmp(const char *thefilename, char *bid) 77 | { 78 | FILE *thefile; 79 | struct stat statbuf; 80 | int i; 81 | 82 | unsigned char * build_id; 83 | char buildid[41]; 84 | 85 | ElfW(Ehdr) *ehdr = 0; 86 | ElfW(Phdr) *phdr = 0; 87 | ElfW(Nhdr) *nhdr = 0; 88 | 89 | if (!(thefile = fopen(thefilename, "r"))) { 90 | perror(thefilename); 91 | exit(EXIT_FAILURE); 92 | } 93 | if (fstat(fileno(thefile), &statbuf) < 0) { 94 | perror(thefilename); 95 | exit(EXIT_FAILURE); 96 | } 97 | 98 | ehdr = (ElfW(Ehdr) *)mmap(0, statbuf.st_size, 99 | PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(thefile), 0); 100 | phdr = (ElfW(Phdr) *)(ehdr->e_phoff + (size_t)ehdr); 101 | 102 | while (phdr->p_type != PT_NOTE) { 103 | ++phdr; 104 | } 105 | 106 | nhdr = (ElfW(Nhdr) *)(phdr->p_offset + (size_t)ehdr); 107 | 108 | while (nhdr->n_type != NT_GNU_BUILD_ID) { 109 | nhdr = (ElfW(Nhdr) *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz + nhdr->n_descsz); 110 | } 111 | 112 | build_id = (unsigned char *)malloc(nhdr->n_descsz); 113 | memcpy(build_id, (void *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz), nhdr->n_descsz); 114 | 115 | for (i = 0; i < nhdr->n_descsz; i++) { 116 | sprintf(buildid+i*2, "%02x",build_id[i]); 117 | } 118 | 119 | free(build_id); 120 | 121 | return strcmp(bid, buildid); 122 | } 123 | 124 | 125 | /* Call into libunwind to determine if function is currently 126 | * on the call-stack. */ 127 | 128 | static int function_on_stack(void *func, size_t func_sz) 129 | { 130 | unw_cursor_t cursor; 131 | unw_context_t uc; 132 | 133 | unw_getcontext(&uc); 134 | unw_init_local(&cursor, &uc); 135 | 136 | do { 137 | unw_word_t ip; 138 | 139 | unw_get_reg(&cursor, UNW_REG_IP, &ip); 140 | 141 | if ( ((void *) ip >= func) && 142 | ((void *) ip < (void *)((char *)func + func_sz)) ) 143 | return -1; 144 | 145 | } while (unw_step (&cursor) > 0); 146 | 147 | return 0; 148 | } 149 | 150 | 151 | static int rewrite_code(void *dest, void *src, void *backup, size_t len) 152 | { 153 | size_t pageSize; 154 | uintptr_t start, end, pageStart; 155 | 156 | /* TODO: worry about trampoline[] spilling into the next page? */ 157 | pageSize = sysconf(_SC_PAGESIZE); 158 | start = (uintptr_t) dest; 159 | end = start + 1; 160 | pageStart = start & -pageSize; 161 | 162 | if (backup) 163 | memcpy(backup, dest, len); 164 | 165 | if (mprotect((void *) pageStart, end - pageStart, PROT_READ | PROT_WRITE | PROT_EXEC)) { 166 | sd_journal_print(LOG_ERR, "Not patching mprotect failed: %s\n", strerror(errno)); 167 | return -1; 168 | } 169 | 170 | memcpy(dest, src, len); 171 | 172 | /* TODO: save and restore, or blindly reset? */ 173 | mprotect((void *) pageStart, end - pageStart, PROT_READ | PROT_EXEC); 174 | 175 | return 0; 176 | } 177 | 178 | 179 | static int fully_loaded; 180 | 181 | void libupatch_load(struct trampolines tramps[]) 182 | { 183 | int i; 184 | 185 | for (i=0; tramps[i].new_addr; i++) { 186 | 187 | if (tramps[i].build_id) { 188 | if (build_id_cmp(tramps[i].map_name, tramps[i].build_id)) { 189 | sd_journal_print(LOG_ERR, "Not patching, buildid mismatch: %s\n", tramps[i].map_name); 190 | return; 191 | } 192 | } 193 | 194 | if (!tramps[i].old_addr) { 195 | 196 | unsigned long addr; 197 | 198 | addr = tramps[i].offset; 199 | 200 | /* TODO: elf headers help us out here? */ 201 | if (tramps[i].type == T_OFFSET_REL) 202 | addr += get_dso_base(tramps[i].map_name, 0); 203 | 204 | tramps[i].old_addr = (void *) addr; 205 | } 206 | sd_journal_print(LOG_INFO, "tramps[%d].old_addr = %p, .new_addr = %p, .oldname = %s\n", 207 | i, tramps[i].old_addr, tramps[i].new_addr, tramps[i].oldname); 208 | 209 | /* Stop now before we break anything */ 210 | if (!tramps[i].new_addr || !tramps[i].old_addr) { 211 | sd_journal_print(LOG_ERR, "Not patching, %s .new_addr=%p, old_addr=%p!\n", 212 | tramps[i].oldname, tramps[i].new_addr, tramps[i].old_addr); 213 | return; 214 | } 215 | if (function_on_stack(tramps[i].old_addr, tramps[i].old_size)) { 216 | sd_journal_print(LOG_ERR, "Not patching, %s is on call stack!\n", 217 | tramps[i].oldname); 218 | return; 219 | } 220 | } 221 | 222 | for (i=0; tramps[i].new_addr; i++) { 223 | 224 | char trampoline[TRAMPOLINE_BYTES]; 225 | 226 | /* trampoline code to our replacement function should look like, 227 | * (ie, when destination virtual address is 0x7ffff79d78d5): 228 | * 229 | * 0: 68 35 78 9d f7 pushq $0xf79d78d5 230 | * 5: c7 44 24 04 ff 7f 00 movl $0x00007fff,0x4(%rsp) 231 | * c: 00 232 | * d: c3 retq 233 | */ 234 | 235 | trampoline[ 0] = 0x68; 236 | trampoline[ 1] = (unsigned long) tramps[i].new_addr & 0xFF; 237 | trampoline[ 2] = (unsigned long) tramps[i].new_addr >> 8 & 0xFF; 238 | trampoline[ 3] = (unsigned long) tramps[i].new_addr >> 16 & 0xFF; 239 | trampoline[ 4] = (unsigned long) tramps[i].new_addr >> 24 & 0xFF; 240 | 241 | trampoline[ 5] = 0xc7; 242 | trampoline[ 6] = 0x44; 243 | trampoline[ 7] = 0x24; 244 | trampoline[ 8] = 0x04; 245 | trampoline[ 9] = (unsigned long) tramps[i].new_addr >> 32 & 0xFF; 246 | trampoline[10] = (unsigned long) tramps[i].new_addr >> 40 & 0xFF; 247 | trampoline[11] = (unsigned long) tramps[i].new_addr >> 48 & 0xFF; 248 | trampoline[12] = (unsigned long) tramps[i].new_addr >> 56 & 0xFF; 249 | 250 | trampoline[13] = 0xc3; 251 | trampoline[14] = 0; 252 | trampoline[15] = 0; 253 | 254 | if (rewrite_code(tramps[i].old_addr, trampoline, tramps[i].old_code, TRAMPOLINE_BYTES)) 255 | continue; 256 | 257 | tramps[i].patched = 1; 258 | } 259 | 260 | fully_loaded = 1; 261 | } 262 | 263 | void libupatch_unload(struct trampolines tramps[]) 264 | { 265 | int i; 266 | 267 | if (!fully_loaded) 268 | return; 269 | 270 | /* Restore the code that we trampled with our trampolines. */ 271 | 272 | for (i=0; tramps[i].new_addr; i++) { 273 | if (tramps[i].patched) 274 | rewrite_code(tramps[i].old_addr, tramps[i].old_code, NULL, TRAMPOLINE_BYTES); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /ptrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ptrace.h" 11 | 12 | /* 13 | * ptrace_attach() 14 | * 15 | * Use ptrace() to attach to a process. This requires calling waitpid() to 16 | * determine when the process is ready to be traced. 17 | * 18 | * args: 19 | * - int pid: pid of the process to attach to 20 | * 21 | */ 22 | 23 | void ptrace_attach(pid_t target) 24 | { 25 | int waitpidstatus; 26 | 27 | if(ptrace(PTRACE_ATTACH, target, NULL, NULL) == -1) 28 | { 29 | fprintf(stderr, "ptrace(PTRACE_ATTACH) failed: %s\n", strerror(errno)); 30 | exit(1); 31 | } 32 | 33 | if(waitpid(target, &waitpidstatus, WUNTRACED) != target) 34 | { 35 | fprintf(stderr, "waitpid(%d) failed: %s\n", target, strerror(errno)); 36 | exit(1); 37 | } 38 | } 39 | 40 | /* 41 | * ptrace_detach() 42 | * 43 | * Detach from a process that is being ptrace()d. Unlike ptrace_cont(), this 44 | * completely ends our relationship with the target process. 45 | * 46 | * args: 47 | * - int pid: pid of the process to detach from. this process must already be 48 | * ptrace()d by us in order for this to work. 49 | * 50 | */ 51 | 52 | void ptrace_detach(pid_t target) 53 | { 54 | if(ptrace(PTRACE_DETACH, target, NULL, NULL) == -1) 55 | { 56 | fprintf(stderr, "ptrace(PTRACE_DETACH) failed: %s\n", strerror(errno)); 57 | exit(1); 58 | } 59 | } 60 | 61 | /* 62 | * ptrace_getregs() 63 | * 64 | * Use ptrace() to get a process' current register state. Uses REG_TYPE 65 | * preprocessor macro in order to allow for both ARM and x86/x86_64 66 | * functionality. 67 | * 68 | * args: 69 | * - int pid: pid of the target process 70 | * - struct REG_TYPE* regs: a struct (either user_regs_struct or user_regs, 71 | * depending on architecture) to store the resulting register data in 72 | * 73 | */ 74 | 75 | void ptrace_getregs(pid_t target, struct REG_TYPE* regs) 76 | { 77 | if(ptrace(PTRACE_GETREGS, target, NULL, regs) == -1) 78 | { 79 | fprintf(stderr, "ptrace(PTRACE_GETREGS) failed: %s\n", strerror(errno)); 80 | exit(1); 81 | } 82 | } 83 | 84 | /* 85 | * ptrace_cont() 86 | * 87 | * Continue the execution of a process being traced using ptrace(). Note that 88 | * this is different from ptrace_detach(): we still retain control of the 89 | * target process after this call. 90 | * 91 | * args: 92 | * - int pid: pid of the target process 93 | * 94 | */ 95 | 96 | void ptrace_cont(pid_t target) 97 | { 98 | struct timespec* sleeptime = malloc(sizeof(struct timespec)); 99 | 100 | sleeptime->tv_sec = 0; 101 | sleeptime->tv_nsec = 5000000; 102 | 103 | if(ptrace(PTRACE_CONT, target, NULL, NULL) == -1) 104 | { 105 | fprintf(stderr, "ptrace(PTRACE_CONT) failed: %s\n", strerror(errno)); 106 | exit(1); 107 | } 108 | 109 | nanosleep(sleeptime, NULL); 110 | 111 | // make sure the target process received SIGTRAP after stopping. 112 | checktargetsig(target); 113 | } 114 | 115 | /* 116 | * ptrace_setregs() 117 | * 118 | * Use ptrace() to set the target's register state. 119 | * 120 | * args: 121 | * - int pid: pid of the target process 122 | * - struct REG_TYPE* regs: a struct (either user_regs_struct or user_regs, 123 | * depending on architecture) containing the register state to be set in the 124 | * target process 125 | * 126 | */ 127 | 128 | void ptrace_setregs(pid_t target, struct REG_TYPE* regs) 129 | { 130 | if(ptrace(PTRACE_SETREGS, target, NULL, regs) == -1) 131 | { 132 | fprintf(stderr, "ptrace(PTRACE_SETREGS) failed: %s\n", strerror(errno)); 133 | exit(1); 134 | } 135 | } 136 | 137 | /* 138 | * ptrace_getsiginfo() 139 | * 140 | * Use ptrace() to determine what signal was most recently raised by the target 141 | * process. This is primarily used for to determine whether the target process 142 | * has segfaulted. 143 | * 144 | * args: 145 | * - int pid: pid of the target process 146 | * 147 | * returns: 148 | * - a siginfo_t containing information about the most recent signal raised by 149 | * the target process 150 | * 151 | */ 152 | 153 | siginfo_t ptrace_getsiginfo(pid_t target) 154 | { 155 | siginfo_t targetsig; 156 | if(ptrace(PTRACE_GETSIGINFO, target, NULL, &targetsig) == -1) 157 | { 158 | fprintf(stderr, "ptrace(PTRACE_GETSIGINFO) failed: %s\n", strerror(errno)); 159 | exit(1); 160 | } 161 | return targetsig; 162 | } 163 | 164 | /* 165 | * ptrace_read() 166 | * 167 | * Use ptrace() to read the contents of a target process' address space. 168 | * 169 | * args: 170 | * - int pid: pid of the target process 171 | * - unsigned long addr: the address to start reading from 172 | * - void *vptr: a pointer to a buffer to read data into 173 | * - int len: the amount of data to read from the target 174 | * 175 | */ 176 | 177 | void ptrace_read(int pid, unsigned long addr, void *vptr, int len) 178 | { 179 | int bytesRead = 0; 180 | int i = 0; 181 | long word = 0; 182 | long *ptr = (long *) vptr; 183 | 184 | while (bytesRead < len) 185 | { 186 | word = ptrace(PTRACE_PEEKTEXT, pid, addr + bytesRead, NULL); 187 | if(word == -1) 188 | { 189 | fprintf(stderr, "ptrace(PTRACE_PEEKTEXT) failed: %s\n", strerror(errno)); 190 | exit(1); 191 | } 192 | bytesRead += sizeof(word); 193 | ptr[i++] = word; 194 | } 195 | } 196 | 197 | /* 198 | * ptrace_write() 199 | * 200 | * Use ptrace() to write to the target process' address space. 201 | * 202 | * args: 203 | * - int pid: pid of the target process 204 | * - unsigned long addr: the address to start writing to 205 | * - void *vptr: a pointer to a buffer containing the data to be written to the 206 | * target's address space 207 | * - int len: the amount of data to write to the target 208 | * 209 | */ 210 | 211 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len) 212 | { 213 | int byteCount = 0; 214 | long word = 0; 215 | 216 | while (byteCount < len) 217 | { 218 | memcpy(&word, vptr + byteCount, sizeof(word)); 219 | word = ptrace(PTRACE_POKETEXT, pid, addr + byteCount, word); 220 | if(word == -1) 221 | { 222 | fprintf(stderr, "ptrace(PTRACE_POKETEXT) failed: %s\n", strerror(errno)); 223 | exit(1); 224 | } 225 | byteCount += sizeof(word); 226 | } 227 | } 228 | 229 | /* 230 | * checktargetsig() 231 | * 232 | * Check what signal was most recently returned by the target process being 233 | * ptrace()d. We expect a SIGTRAP from the target process, so raise an error 234 | * and exit if we do not receive that signal. The most likely non-SIGTRAP 235 | * signal for us to receive would be SIGSEGV. 236 | * 237 | * args: 238 | * - int pid: pid of the target process 239 | * 240 | */ 241 | 242 | void checktargetsig(int pid) 243 | { 244 | // check the signal that the child stopped with. 245 | siginfo_t targetsig = ptrace_getsiginfo(pid); 246 | 247 | // if it wasn't SIGTRAP, then something bad happened (most likely a 248 | // segfault). 249 | if(targetsig.si_signo != SIGTRAP) 250 | { 251 | fprintf(stderr, "instead of expected SIGTRAP, target stopped with signal %d: %s\n", targetsig.si_signo, strsignal(targetsig.si_signo)); 252 | fprintf(stderr, "sending process %d a SIGSTOP signal for debugging purposes\n", pid); 253 | ptrace(PTRACE_CONT, pid, NULL, SIGSTOP); 254 | exit(1); 255 | } 256 | } 257 | 258 | /* 259 | * restoreStateAndDetach() 260 | * 261 | * Once we're done debugging a target process, restore the process' backed-up 262 | * data and register state and let it go on its merry way. 263 | * 264 | * args: 265 | * - pid_t target: pid of the target process 266 | * - unsigned long addr: address within the target's address space to write 267 | * backed-up data to 268 | * - void* backup: a buffer pointing to the backed-up data 269 | * - int datasize: the amount of backed-up data to write 270 | * - struct REG_TYPE oldregs: backed-up register state to restore 271 | * 272 | */ 273 | 274 | void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs) 275 | { 276 | ptrace_write(target, addr, backup, datasize); 277 | ptrace_setregs(target, &oldregs); 278 | ptrace_detach(target); 279 | } 280 | -------------------------------------------------------------------------------- /patches/GHOST.md: -------------------------------------------------------------------------------- 1 | # upatch demo - CVE-2015-0235 aka GHOST 2 | ## Proof-of-concept for patching a running process 3 | 4 | A heap-based buffer overflow was found in glibc's 5 | ```__nss_hostname_digits_dots()``` function, which is used by the 6 | ```mgethostbyname()``` and ```gethostbyname2()``` glibc function calls. A 7 | remote attacker able to make an application call either of these functions 8 | could use this flaw to execute arbitrary code with the permissions of the user 9 | running the application. 10 | 11 | In this demo, we will build a shared library patch that un-patches glibc. We 12 | do this backwards as a matter of convenience -- it's easier to patch the 13 | currently installed glibc than to install or downgrade to a vulnerable 14 | version. The same mechanics would apply if we were actually patching a 15 | vulnerability. 16 | 17 | 18 | ## Step 0 - Find a vulnerability detection program 19 | 20 | Qualys, who reported the bug, introduced a small test program called GHOST.c 21 | that checks whether a system is vulnerable or not. See the source in section 22 | 4 - Case Studies from their [full 23 | report](http://www.openwall.com/lists/oss-security/2015/01/27/9). 24 | 25 | I modified GHOST.c to continuously loop, performing its check and then 26 | sleeping for a few seconds. This test program will be targeted to patch. On 27 | modern RHEL7, it should report "not vulnerable". This demo will un-patch the 28 | GHOST fix from this process, after which it should report "vulnerable". 29 | 30 | 31 | ## Step 1 - Build glibc 32 | 33 | Download and build the glibc package so we can steal the compilation 34 | arguments. Here's the rpmbuild build line for ```digits_dots.c```: 35 | 36 | gcc \ 37 | digits_dots.c \ 38 | -c \ 39 | -std=gnu99 \ 40 | -fgnu89-inline \ 41 | \ 42 | -DNDEBUG \ 43 | -O3 \ 44 | -Wall \ 45 | -Winline \ 46 | -Wwrite-strings \ 47 | -fasynchronous-unwind-tables \ 48 | -fmerge-all-constants \ 49 | -frounding-math \ 50 | -g \ 51 | -mtune=generic \ 52 | -Wstrict-prototypes \ 53 | -I../include \ 54 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/build-x86_64-redhat-linux/nss \ 55 | -I/root/rpmbuild/BUILD/glibc-2.17-c758a686/build-x86_64-redhat-linux \ 56 | -I../sysdeps/unix/sysv/linux/x86_64/64/nptl \ 57 | -I../sysdeps/unix/sysv/linux/x86_64/64 \ 58 | -I../nptl/sysdeps/unix/sysv/linux/x86_64 \ 59 | -I../nptl/sysdeps/unix/sysv/linux/x86 \ 60 | -I../sysdeps/unix/sysv/linux/x86 \ 61 | -I../rtkaio/sysdeps/unix/sysv/linux/x86_64 \ 62 | -I../sysdeps/unix/sysv/linux/x86_64 \ 63 | -I../sysdeps/unix/sysv/linux/wordsize-64 \ 64 | -I../ports/sysdeps/unix/sysv/linux \ 65 | -I../nptl/sysdeps/unix/sysv/linux \ 66 | -I../nptl/sysdeps/pthread \ 67 | -I../rtkaio/sysdeps/pthread \ 68 | -I../sysdeps/pthread \ 69 | -I../rtkaio/sysdeps/unix/sysv/linux \ 70 | -I../sysdeps/unix/sysv/linux \ 71 | -I../sysdeps/gnu \ 72 | -I../sysdeps/unix/inet \ 73 | -I../ports/sysdeps/unix/sysv \ 74 | -I../nptl/sysdeps/unix/sysv \ 75 | -I../rtkaio/sysdeps/unix/sysv \ 76 | -I../sysdeps/unix/sysv \ 77 | -I../sysdeps/unix/x86_64 \ 78 | -I../ports/sysdeps/unix \ 79 | -I../nptl/sysdeps/unix \ 80 | -I../rtkaio/sysdeps/unix \ 81 | -I../sysdeps/unix \ 82 | -I../sysdeps/posix \ 83 | -I../nptl/sysdeps/x86_64/64 \ 84 | -I../sysdeps/x86_64/64 \ 85 | -I../sysdeps/x86_64/fpu/multiarch \ 86 | -I../sysdeps/x86_64/fpu \ 87 | -I../sysdeps/x86/fpu \ 88 | -I../sysdeps/x86_64/multiarch \ 89 | -I../nptl/sysdeps/x86_64 \ 90 | -I../sysdeps/x86_64 \ 91 | -I../sysdeps/x86 \ 92 | -I../sysdeps/ieee754/ldbl-96 \ 93 | -I../sysdeps/ieee754/dbl-64/wordsize-64 \ 94 | -I../sysdeps/ieee754/dbl-64 \ 95 | -I../sysdeps/ieee754/flt-32 \ 96 | -I../sysdeps/wordsize-64 \ 97 | -I../sysdeps/ieee754 \ 98 | -I../sysdeps/generic \ 99 | -I../ports \ 100 | -I../nptl \ 101 | -I../rtkaio \ 102 | -I.. \ 103 | -I../libio \ 104 | -I. \ 105 | -nostdinc \ 106 | -isystem \ 107 | /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include \ 108 | -isystem \ 109 | /usr/include \ 110 | -D_LIBC_REENTRANT \ 111 | -include \ 112 | /root/rpmbuild/BUILD/glibc-2.17-c758a686/build-x86_64-redhat-linux/libc-modules.h \ 113 | -DMODULE_NAME=libc \ 114 | -include \ 115 | ../include/libc-symbols.h 116 | 117 | In this demo, I adjusted all of the include paths accordingly. A few other 118 | build options I needed to change: 119 | 120 | 1. ```-fPIC``` - our changes will end up in a shared library, so we need to 121 | generate position independent code. 122 | 2. Remove the ```-DMODULE_NAME=libc``` definition, else dlopen was throwing a 123 | ```undefined symbol: __libc_tsd_CTYPE_B``` error when attempting to load the 124 | shared library. 125 | 126 | 127 | ## Step 2 - Download glibc git tree 128 | 129 | Grab a copy of the upstream git tree so we build an unpatched version of 130 | ```digits_dots.c``` pre-GHOST fix. 131 | 132 | The GHOST vulnerability was fixed by the following commit: 133 | 134 | [d5dd6189d506 Fix parsing of numeric hosts in 135 | gethostbyname_r](https://sourceware.org/git/?p=glibc.git;a=commit;h=d5dd6189d506068ed11c8bfa1e1e9bffde04decd) 136 | 137 | So we'll be building the shared library with ```d5dd6189d506~1:nss/digits_dots.c``` 138 | 139 | 140 | ## Step 3 - Adjust the patches Makefile 141 | 142 | Add the giant gcc line from the rpmbuild to the Makefile with the necessary 143 | updates (```-fPIC```, no ```-DMODULE_NAME=libc```, and updated include paths). 144 | 145 | Also update the Makefile's ```TRAMPOLINES``` list to include: 146 | 147 | TRAMPOLINES := \ 148 | /usr/lib64/libc.so.6 __nss_hostname_digits_dots 149 | 150 | The build will auto-generate ```trampolines.c``` with the glibc's build id and 151 | ```__nss_hostname_digits_dots``` offsets and size: 152 | 153 | #include 154 | #include "trampoline.h" 155 | 156 | extern void *patched___nss_hostname_digits_dots; 157 | 158 | struct trampolines tramps[] = { 159 | 160 | { .map_name = "/usr/lib64/libc-2.17.so", 161 | .build_id = "4ec510505f71aeacdf5a4035bec5b39afbb65538", 162 | .oldname = "__nss_hostname_digits_dots", 163 | .offset = 0x000000000010a4c0, 164 | .old_size = 0x0000000000000522, 165 | .new_addr = &patched___nss_hostname_digits_dots, 166 | .type = T_OFFSET_REL, }, 167 | 168 | { 0 } }; 169 | 170 | 171 | ## Step 4 - Shared library dry-run 172 | 173 | The [linux-inject](https://github.com/gaffe23/linux-inject) project was 174 | supplemented with a feature that attempts to dlopen the shared library patch 175 | with the ```RTLD_NOW``` flag set. This instructs the dynamic linker to 176 | resolve all undefined symbols at load time. 177 | 178 | In this case, ```patched___nss_hostname_digits_dots``` calls 179 | ```__inet_aton```, which happens to be an internal glibc symbol. 180 | 181 | There are a few ways to solve this issue: 182 | 183 | 1. Use public interfaces were available (```inet_aton``` for example) 184 | 2. Function pointers 185 | 3. Carry our own version 186 | 187 | For demonstration purposes, the third solution was simple enough. Luckily 188 | ```__inet_aton``` did not call any other glibc private routines, so a simple 189 | copy-paste from ```resolv/inet_addr.c``` was sufficient. All other functions 190 | that ```__inet_aton``` called (```isdigit```, ```isspace```, etc) are exported 191 | by glibc, so the dynamic linker happily resolved those. 192 | 193 | 194 | ## Step 5 - Success! 195 | 196 | After injecting the shared library "un-patch" to the running GHOST process, 197 | it's report changed from "not vulnerable" to "vulnerable". 198 | 199 | Here's a .gif capture of the entire process, sans rpmbuild and git tree 200 | cloning: 201 | 202 | ![ghost_demo](ghost_demo.gif) 203 | 204 | 205 | ## Step 6 - What's next 206 | 207 | Although this demo was successful, it remains to be seen how useful the shared 208 | library injection may be used against real-life vulnerabilities and running 209 | processes. On a RHEL7 system, I had no success injecting the GHOST shared 210 | library un-patch into any binaries that call ```gethostbyname```: 211 | 212 | % for f in /sbin/*; do objdump $f -DS 2>/dev/null | grep -q gethostbyname && echo "$(basename $f) - $(pgrep $(basename $f))"; done 213 | 214 | arp 215 | auditd - 303 804 216 | ptrace(PTRACE_ATTACH) failed 217 | 218 | instead of expected SIGTRAP, target stopped with signal 11: Segmentation fault 219 | sending process 804 a SIGSTOP signal for debugging purposes 220 | 221 | aureport 222 | ausearch 223 | dhclient - 915 224 | __libc_dlopen_mode() failed 225 | 226 | ether-wake 227 | exportfs 228 | ifconfig 229 | mount.nfs 230 | mount.nfs4 231 | ntpd 232 | postalias 233 | postconf 234 | postmap 235 | postqueue 236 | pppd 237 | route 238 | rpc.mountd 239 | rsyslogd - 1194 240 | __libc_dlopen_mode() failed 241 | 242 | sendmail - 2358 2382 243 | __libc_dlopen_mode() failed 244 | 245 | sendmail.sendmail 246 | ss 247 | sshd - 1300 3452 7141 28805 28863 29367 29400 30434 248 | __libc_dlopen_mode() failed 249 | 250 | tcpd 251 | tcpdmatch 252 | tcsd 253 | try-from 254 | umount.nfs 255 | umount.nfs4 256 | 257 | This highlights the number of potential points of failure in this technique: 258 | 259 | 1. Target process must be ptrace-able to change code, get signal info 260 | 2. Must be able to call dlopen from target 261 | 3. Shared library patch must be able to patch locations 262 | 4. ??? 263 | 264 | For now, the next step is to modify 265 | [linux-inject](https://github.com/gaffe23/linux-inject) to also call 266 | ```dlerror``` so we can get some visibility into why ```dlopen``` consistently 267 | fails against real world programs. 268 | 269 | 270 | ## Step 6 - Update 271 | 272 | After a bit of debugging (I attached to a running dhclient, then from gdb 273 | manually called dlopen and dlerror) I figured out that SELinux was preventing 274 | me these processes from opening the shared library patch. With apologies to 275 | Dan Walsh, I set SELinux to permissive mode and viola -- I could inject the 276 | GHOST vulnerability .so and verify it in each processes' /proc/PID/maps file. 277 | 278 | (Note: auditd still fails the ptrace even with SELinux turned off). 279 | -------------------------------------------------------------------------------- /patches/CVE-2015-0240/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # upatch specifics 3 | # 4 | # based on: 5 | # samba-4.1.12-17.el7.src.rpm 6 | 7 | UPATCH=cve_2015_0240 8 | TRAMPOLINES := \ 9 | /usr/lib64/samba/libsmbd_base.so netr_creds_server_step_check \ 10 | /usr/lib64/samba/libsmbd_base.so _netr_ServerPasswordSet 11 | 12 | PATCH_SRC=patches.c 13 | 14 | patches.o: patches.c 15 | gcc \ 16 | -g \ 17 | -pipe \ 18 | -Wall \ 19 | -fexceptions \ 20 | -fstack-protector-strong \ 21 | --param=ssp-buffer-size=4 \ 22 | -grecord-gcc-switches \ 23 | -m64 \ 24 | -mtune=generic \ 25 | -fPIC \ 26 | -fstack-protector \ 27 | -D_REENTRANT \ 28 | -D_POSIX_PTHREAD_SEMANTICS \ 29 | -DSTATIC_RPC_NETLOGON_MODULES=NULL \ 30 | -DSTATIC_RPC_NETLOGON_MODULES_PROTO= \ 31 | -MD \ 32 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/rpc_server \ 33 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/rpc_server \ 34 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3 \ 35 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3 \ 36 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/include \ 37 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/include \ 38 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/lib \ 39 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/lib \ 40 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/tdb_compat \ 41 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/tdb_compat \ 42 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/include/public \ 43 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../include/public \ 44 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4 \ 45 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4 \ 46 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib \ 47 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib \ 48 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/lib \ 49 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/lib \ 50 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/include \ 51 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/include \ 52 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/include \ 53 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../include \ 54 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/replace \ 55 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/replace \ 56 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default \ 57 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/.. \ 58 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/param \ 59 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/param \ 60 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/lsarpc \ 61 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/lsarpc \ 62 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/ldap \ 63 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/ldap \ 64 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/auth \ 65 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/auth \ 66 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/librpc \ 67 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../librpc \ 68 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/nbt \ 69 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/nbt \ 70 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/dsdb \ 71 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/dsdb \ 72 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/auth \ 73 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/auth \ 74 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/librpc \ 75 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/librpc \ 76 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/addns \ 77 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/addns \ 78 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/auth/gensec \ 79 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../auth/gensec \ 80 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/auth/credentials \ 81 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../auth/credentials \ 82 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/krb5_wrap \ 83 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/krb5_wrap \ 84 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/ntdb \ 85 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/ntdb \ 86 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/dns \ 87 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/dns \ 88 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/lib/socket \ 89 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/lib/socket \ 90 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/registry \ 91 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/registry \ 92 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/ccan \ 93 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/ccan \ 94 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/util \ 95 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/util \ 96 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/auth/kerberos \ 97 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/auth/kerberos \ 98 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/param \ 99 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/param \ 100 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/socket \ 101 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/socket \ 102 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/util/charset \ 103 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/util/charset \ 104 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/lib/events \ 105 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/lib/events \ 106 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/librpc \ 107 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/librpc \ 108 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/async_req \ 109 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/async_req \ 110 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/auth/gensec \ 111 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/auth/gensec \ 112 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/ldb-samba \ 113 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/ldb-samba \ 114 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/nsswitch/libwbclient \ 115 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../nsswitch/libwbclient \ 116 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libds/common \ 117 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libds/common \ 118 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/auth/kerberos \ 119 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../auth/kerberos \ 120 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/auth \ 121 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/auth \ 122 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/dbwrap \ 123 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/dbwrap \ 124 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/lib/pthreadpool \ 125 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/lib/pthreadpool \ 126 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/netlogon \ 127 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/netlogon \ 128 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/security \ 129 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/security \ 130 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/smbconf \ 131 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/smbconf \ 132 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/nsswitch \ 133 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../nsswitch \ 134 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/drsuapi \ 135 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/drsuapi \ 136 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/tsocket \ 137 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/tsocket \ 138 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/lib/tls \ 139 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/lib/tls \ 140 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/auth/ntlmssp \ 141 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../auth/ntlmssp \ 142 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/auth \ 143 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../auth \ 144 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/cldap \ 145 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/cldap \ 146 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/libcli \ 147 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/libcli \ 148 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/smb \ 149 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/smb \ 150 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/libcli/named_pipe_auth \ 151 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../libcli/named_pipe_auth \ 152 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/libcli/ldap \ 153 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/libcli/ldap \ 154 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/dynconfig \ 155 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../dynconfig \ 156 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source3/param \ 157 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source3/param \ 158 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/compression \ 159 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/compression \ 160 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/source4/lib/stream \ 161 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../source4/lib/stream \ 162 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/default/lib/crypto \ 163 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin/../lib/crypto \ 164 | -I/root/rpmbuild/BUILD/samba-4.1.12/bin//usr/local/include \ 165 | -D_SAMBA_BUILD_=4 \ 166 | -DHAVE_CONFIG_H=1 \ 167 | -D_GNU_SOURCE=1 \ 168 | -D_XOPEN_SOURCE_EXTENDED=1 \ 169 | patches.c \ 170 | -c \ 171 | -o \ 172 | patches.o 173 | 174 | # 175 | # upatch boilerplate 176 | # 177 | 178 | .DEFAULT_GOAL:=all 179 | all: libupatch_$(UPATCH).so 180 | 181 | CFLAGS=-fPIC -Wall -ggdb -MP -MD 182 | 183 | libupatch_$(UPATCH).so: $(PATCH_SRC:.c=.o) trampolines.o 184 | $(CC) -shared -lupatch -lsystemd -Wl,-soname,$@ -o $@ $^ 185 | 186 | -include trampolines.d 187 | 188 | trampolines.c: 189 | ../gen_trampolines ${TRAMPOLINES} > $@ 190 | 191 | install: all 192 | install -D libupatch_$(UPATCH).so /usr/lib64 193 | ldconfig -n /usr/lib64 194 | 195 | uninstall: 196 | $(RM) /usr/lib64/libupatch_$(UPATCH).so 197 | ldconfig -n /usr/lib64 198 | 199 | clean: 200 | $(RM) -f trampolines.c *.d *.o *.so 201 | 202 | .PHONY: install uninstall clean 203 | -------------------------------------------------------------------------------- /inject-x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | #include "ptrace.h" 10 | 11 | /* 12 | * injectSharedLibrary() 13 | * 14 | * This is the code that will actually be injected into the target process. 15 | * This code is responsible for loading the shared library into the target 16 | * process' address space. First, it calls malloc() to allocate a buffer to 17 | * hold the filename of the library to be loaded. Then, it calls 18 | * __libc_dlopen_mode(), libc's implementation of dlopen(), to load the desired 19 | * shared library. Finally, it calls free() to free the buffer containing the 20 | * library name. Each time it needs to give control back to the injector 21 | * process, it breaks back in by executing an "int $3" instruction. See the 22 | * comments below for more details on how this works. 23 | * 24 | */ 25 | 26 | void injectSharedLibrary(long mallocaddr, long freeaddr, long dlopenaddr) 27 | { 28 | // here are the assumptions I'm making about what data will be located 29 | // where at the time the target executes this code: 30 | // 31 | // ebx = address of malloc() in target process 32 | // edi = address of __libc_dlopen_mode() in target process 33 | // esi = address of free() in target process 34 | // ecx = size of the path to the shared library we want to load 35 | 36 | // for some reason it's adding 1 to esi, so subtract 1 from it 37 | asm("dec %esi"); 38 | 39 | // call malloc() from within the target process 40 | asm( 41 | // choose the amount of memory to allocate with malloc() based on the size 42 | // of the path to the shared library passed via ecx 43 | "push %ecx \n" 44 | // call malloc 45 | "call *%ebx \n" 46 | // copy malloc's return value (i.e. the address of the allocated buffer) into ebx 47 | "mov %eax, %ebx \n" 48 | // break back in so that the injector can get the return value 49 | "int $3" 50 | ); 51 | 52 | // call __libc_dlopen_mode() to load the shared library 53 | asm( 54 | // 2nd argument to __libc_dlopen_mode(): flag = RTLD_LAZY 55 | "push $1 \n" 56 | // 1st argument to __libc_dlopen_mode(): filename = the buffer we allocated earlier 57 | "push %ebx \n" 58 | // call __libc_dlopen_mode() 59 | "call *%edi \n" 60 | // break back in so that the injector can check the return value 61 | "int $3" 62 | ); 63 | 64 | // call free() on the previously malloc'd buffer 65 | asm( 66 | // 1st argument to free(): ptr = the buffer we allocated earlier 67 | "push %ebx \n" 68 | // call free() 69 | "call *%esi" 70 | ); 71 | 72 | // we already overwrote the RET instruction at the end of this function 73 | // with an INT 3, so at this point the injector will regain control of 74 | // the target's execution. 75 | } 76 | 77 | /* 78 | * injectSharedLibrary_end() 79 | * 80 | * This function's only purpose is to be contiguous to injectSharedLibrary(), 81 | * so that we can use its address to more precisely figure out how long 82 | * injectSharedLibrary() is. 83 | * 84 | */ 85 | 86 | void injectSharedLibrary_end() 87 | { 88 | } 89 | 90 | int main(int argc, char** argv) 91 | { 92 | if(argc < 4) 93 | { 94 | usage(argv[0]); 95 | return 1; 96 | } 97 | 98 | char* command = argv[1]; 99 | char* commandArg = argv[2]; 100 | char* libname = argv[3]; 101 | char* libPath = realpath(libname, NULL); 102 | 103 | char* processName = NULL; 104 | pid_t target = 0; 105 | 106 | if(!libPath) 107 | { 108 | fprintf(stderr, "can't find file \"%s\"\n", libname); 109 | return 1; 110 | } 111 | 112 | if(!strcmp(command, "-n")) 113 | { 114 | processName = commandArg; 115 | target = findProcessByName(processName); 116 | if(target == -1) 117 | { 118 | fprintf(stderr, "doesn't look like a process named \"%s\" is running right now\n", processName); 119 | return 1; 120 | } 121 | 122 | printf("targeting process \"%s\" with pid %d\n", processName, target); 123 | } 124 | else if(!strcmp(command, "-p")) 125 | { 126 | target = atoi(commandArg); 127 | printf("targeting process with pid %d\n", target); 128 | } 129 | else 130 | { 131 | usage(argv[0]); 132 | return 1; 133 | } 134 | 135 | int libPathLength = strlen(libPath) + 1; 136 | 137 | int mypid = getpid(); 138 | long mylibcaddr = getlibcaddr(mypid); 139 | 140 | // find the addresses of the syscalls that we'd like to use inside the 141 | // target, as loaded inside THIS process (i.e. NOT the target process) 142 | long mallocAddr = getFunctionAddress("malloc"); 143 | long freeAddr = getFunctionAddress("free"); 144 | long dlopenAddr = getFunctionAddress("__libc_dlopen_mode"); 145 | 146 | // use the base address of libc to calculate offsets for the syscalls 147 | // we want to use 148 | long mallocOffset = mallocAddr - mylibcaddr; 149 | long freeOffset = freeAddr - mylibcaddr; 150 | long dlopenOffset = dlopenAddr - mylibcaddr; 151 | 152 | // get the target process' libc address and use it to find the 153 | // addresses of the syscalls we want to use inside the target process 154 | long targetLibcAddr = getlibcaddr(target); 155 | long targetMallocAddr = targetLibcAddr + mallocOffset; 156 | long targetFreeAddr = targetLibcAddr + freeOffset; 157 | long targetDlopenAddr = targetLibcAddr + dlopenOffset; 158 | 159 | struct user_regs_struct oldregs, regs; 160 | memset(&oldregs, 0, sizeof(struct user_regs_struct)); 161 | memset(®s, 0, sizeof(struct user_regs_struct)); 162 | 163 | ptrace_attach(target); 164 | 165 | ptrace_getregs(target, &oldregs); 166 | memcpy(®s, &oldregs, sizeof(struct user_regs_struct)); 167 | 168 | // find a good address to copy code to 169 | long addr = freespaceaddr(target) + sizeof(long); 170 | 171 | // now that we have an address to copy code to, set the target's eip to it. 172 | regs.eip = addr; 173 | 174 | // pass arguments to my function injectSharedLibrary() by loading them 175 | // into the right registers. see comments in injectSharedLibrary() for 176 | // more details. 177 | regs.ebx = targetMallocAddr; 178 | regs.edi = targetDlopenAddr; 179 | regs.esi = targetFreeAddr; 180 | regs.ecx = libPathLength; 181 | ptrace_setregs(target, ®s); 182 | 183 | // figure out the size of injectSharedLibrary() so we know how big of a buffer to allocate. 184 | size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)injectSharedLibrary; 185 | 186 | // also figure out where the RET instruction at the end of 187 | // injectSharedLibrary() lies so that we can overwrite it with an INT 3 188 | // in order to break back into the target process. gcc and clang both 189 | // force function addresses to be word-aligned, which means that 190 | // functions are padded at the end after the RET instruction that ends 191 | // the function. as a result, even though in theory we've found the 192 | // length of the function, it is very likely padded with NOPs, so we 193 | // still need to do a bit of searching to find the RET. 194 | intptr_t injectSharedLibrary_ret = (intptr_t)findRet(injectSharedLibrary_end) - (intptr_t)injectSharedLibrary; 195 | 196 | // back up whatever data used to be at the address we want to modify. 197 | char* backup = malloc(injectSharedLibrary_size * sizeof(char)); 198 | ptrace_read(target, addr, backup, injectSharedLibrary_size); 199 | 200 | // set up the buffer containing the code to inject into the target process. 201 | char* newcode = malloc(injectSharedLibrary_size * sizeof(char)); 202 | memset(newcode, 0, injectSharedLibrary_size * sizeof(char)); 203 | 204 | // copy the code of injectSharedLibrary() to a buffer. 205 | memcpy(newcode, injectSharedLibrary, injectSharedLibrary_size - 1); 206 | // overwrite the RET instruction with an INT 3. 207 | newcode[injectSharedLibrary_ret] = INTEL_INT3_INSTRUCTION; 208 | 209 | // copy injectSharedLibrary()'s code to the target address inside the 210 | // target process' address space. 211 | ptrace_write(target, addr, newcode, injectSharedLibrary_size); 212 | 213 | // now that the new code is in place, let the target run our injected code. 214 | ptrace_cont(target); 215 | 216 | // at this point, the target should have run malloc(). check its return 217 | // value to see if it succeeded, and bail out cleanly if it didn't. 218 | struct user_regs_struct malloc_regs; 219 | memset(&malloc_regs, 0, sizeof(struct user_regs_struct)); 220 | ptrace_getregs(target, &malloc_regs); 221 | unsigned long targetBuf = malloc_regs.eax; 222 | if(targetBuf == 0) 223 | { 224 | fprintf(stderr, "malloc() failed to allocate memory\n"); 225 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 226 | free(backup); 227 | free(newcode); 228 | return 1; 229 | } 230 | 231 | // if we get here, then malloc likely succeeded, so now we need to copy 232 | // the path to the shared library we want to inject into the buffer 233 | // that the target process just malloc'd. this is needed so that it can 234 | // be passed as an argument to __libc_dlopen_mode later on. 235 | 236 | // read the current value of eax, which contains malloc's return value, 237 | // and copy the name of our shared library to that address inside the 238 | // target process. 239 | ptrace_write(target, targetBuf, libPath, libPathLength); 240 | 241 | // now call __libc_dlopen_mode() to attempt to load the shared library. 242 | ptrace_cont(target); 243 | 244 | // check out what the registers look like after calling 245 | // __libc_dlopen_mode. 246 | struct user_regs_struct dlopen_regs; 247 | memset(&dlopen_regs, 0, sizeof(struct user_regs_struct)); 248 | ptrace_getregs(target, &dlopen_regs); 249 | unsigned long long libAddr = dlopen_regs.eax; 250 | 251 | // if eax is 0 here, then dlopen failed, and we should bail out cleanly. 252 | if(libAddr == 0) 253 | { 254 | fprintf(stderr, "__libc_dlopen_mode() failed to load %s\n", libname); 255 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 256 | free(backup); 257 | free(newcode); 258 | return 1; 259 | } 260 | 261 | // now check /proc/pid/maps to see whether injection was successful. 262 | if(checkloaded(target, libname)) 263 | { 264 | printf("\"%s\" successfully injected\n", libname); 265 | } 266 | else 267 | { 268 | fprintf(stderr, "could not inject \"%s\"\n", libname); 269 | } 270 | 271 | // as a courtesy, free the buffer that we allocated inside the target 272 | // process. we don't really care whether this succeeds, so don't 273 | // bother checking the return value. 274 | ptrace_cont(target); 275 | 276 | // at this point, if everything went according to plan, we've loaded 277 | // the shared library inside the target process, so we're done. restore 278 | // the old state and detach from the target. 279 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 280 | free(backup); 281 | free(newcode); 282 | 283 | return 0; 284 | } 285 | -------------------------------------------------------------------------------- /patches/CVE-2015-3185/patches.c: -------------------------------------------------------------------------------- 1 | /* Licensed to the Apache Software Foundation (ASF) under one or more 2 | * contributor license agreements. See the NOTICE file distributed with 3 | * this work for additional information regarding copyright ownership. 4 | * The ASF licenses this file to You under the Apache License, Version 2.0 5 | * (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * @file request.c 19 | * @brief functions to get and process requests 20 | * 21 | * @author Rob McCool 3/21/93 22 | * 23 | * Thoroughly revamped by rst for Apache. NB this file reads 24 | * best from the bottom up. 25 | * 26 | */ 27 | 28 | #include "apr_strings.h" 29 | #include "apr_file_io.h" 30 | #include "apr_fnmatch.h" 31 | 32 | #define APR_WANT_STRFUNC 33 | #include "apr_want.h" 34 | 35 | #include "ap_config.h" 36 | #include "ap_provider.h" 37 | #include "httpd.h" 38 | #include "http_config.h" 39 | #include "http_request.h" 40 | #include "http_core.h" 41 | #include "http_protocol.h" 42 | #include "http_log.h" 43 | #include "http_main.h" 44 | #include "util_filter.h" 45 | #include "util_charset.h" 46 | #include "util_script.h" 47 | 48 | #include "ap_expr.h" 49 | #include "mod_request.h" 50 | 51 | #include "mod_core.h" 52 | #include "mod_auth.h" 53 | 54 | #if APR_HAVE_STDARG_H 55 | #include 56 | #endif 57 | 58 | AP_DECLARE_HOOK(int,force_authn,(request_rec *r)) 59 | 60 | APR_HOOK_STRUCT( 61 | APR_HOOK_LINK(force_authn) 62 | ) 63 | 64 | AP_IMPLEMENT_HOOK_RUN_FIRST(int,force_authn, 65 | (request_rec *r), (r), DECLINED) 66 | 67 | 68 | AP_DECLARE(int) ap_some_authn_required(request_rec *r) 69 | { 70 | int access_status; 71 | 72 | switch (ap_satisfies(r)) { 73 | case SATISFY_ALL: 74 | case SATISFY_NOSPEC: 75 | if ((access_status = ap_run_access_checker(r)) != OK) { 76 | break; 77 | } 78 | 79 | access_status = ap_run_access_checker_ex(r); 80 | if (access_status == DECLINED) { 81 | return TRUE; 82 | } 83 | 84 | break; 85 | case SATISFY_ANY: 86 | if ((access_status = ap_run_access_checker(r)) == OK) { 87 | break; 88 | } 89 | 90 | access_status = ap_run_access_checker_ex(r); 91 | if (access_status == DECLINED) { 92 | return TRUE; 93 | } 94 | 95 | break; 96 | } 97 | 98 | return FALSE; 99 | } 100 | 101 | 102 | 103 | static int decl_die(int status, const char *phase, request_rec *r) 104 | { 105 | if (status == DECLINED) { 106 | ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(00025) 107 | "configuration error: couldn't %s: %s", phase, r->uri); 108 | return HTTP_INTERNAL_SERVER_ERROR; 109 | } 110 | else { 111 | ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, 112 | "auth phase '%s' gave status %d: %s", phase, 113 | status, r->uri); 114 | return status; 115 | } 116 | } 117 | 118 | /* This is the master logic for processing requests. Do NOT duplicate 119 | * this logic elsewhere, or the security model will be broken by future 120 | * API changes. Each phase must be individually optimized to pick up 121 | * redundant/duplicate calls by subrequests, and redirects. 122 | */ 123 | AP_DECLARE(int) patched_ap_process_request_internal(request_rec *r) 124 | { 125 | int file_req = (r->main && r->filename); 126 | int access_status; 127 | core_dir_config *d; 128 | 129 | /* Ignore embedded %2F's in path for proxy requests */ 130 | if (!r->proxyreq && r->parsed_uri.path) { 131 | d = ap_get_core_module_config(r->per_dir_config); 132 | if (d->allow_encoded_slashes) { 133 | access_status = ap_unescape_url_keep2f(r->parsed_uri.path, d->decode_encoded_slashes); 134 | } 135 | else { 136 | access_status = ap_unescape_url(r->parsed_uri.path); 137 | } 138 | if (access_status) { 139 | if (access_status == HTTP_NOT_FOUND) { 140 | if (! d->allow_encoded_slashes) { 141 | ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026) 142 | "found %%2f (encoded '/') in URI " 143 | "(decoded='%s'), returning 404", 144 | r->parsed_uri.path); 145 | } 146 | } 147 | return access_status; 148 | } 149 | } 150 | 151 | ap_getparents(r->uri); /* OK --- shrinking transformations... */ 152 | 153 | /* All file subrequests are a huge pain... they cannot bubble through the 154 | * next several steps. Only file subrequests are allowed an empty uri, 155 | * otherwise let translate_name kill the request. 156 | */ 157 | if (!file_req) { 158 | if ((access_status = ap_location_walk(r))) { 159 | return access_status; 160 | } 161 | if ((access_status = ap_if_walk(r))) { 162 | return access_status; 163 | } 164 | 165 | d = ap_get_core_module_config(r->per_dir_config); 166 | if (d->log) { 167 | r->log = d->log; 168 | } 169 | 170 | if ((access_status = ap_run_translate_name(r))) { 171 | return decl_die(access_status, "translate", r); 172 | } 173 | } 174 | 175 | /* Reset to the server default config prior to running map_to_storage 176 | */ 177 | r->per_dir_config = r->server->lookup_defaults; 178 | 179 | if ((access_status = ap_run_map_to_storage(r))) { 180 | /* This request wasn't in storage (e.g. TRACE) */ 181 | return access_status; 182 | } 183 | 184 | /* Rerun the location walk, which overrides any map_to_storage config. 185 | */ 186 | if ((access_status = ap_location_walk(r))) { 187 | return access_status; 188 | } 189 | if ((access_status = ap_if_walk(r))) { 190 | return access_status; 191 | } 192 | 193 | d = ap_get_core_module_config(r->per_dir_config); 194 | if (d->log) { 195 | r->log = d->log; 196 | } 197 | 198 | if ((access_status = ap_run_post_perdir_config(r))) { 199 | return access_status; 200 | } 201 | 202 | /* Only on the main request! */ 203 | if (r->main == NULL) { 204 | if ((access_status = ap_run_header_parser(r))) { 205 | return access_status; 206 | } 207 | } 208 | 209 | /* Skip authn/authz if the parent or prior request passed the authn/authz, 210 | * and that configuration didn't change (this requires optimized _walk() 211 | * functions in map_to_storage that use the same merge results given 212 | * identical input.) If the config changes, we must re-auth. 213 | */ 214 | if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) { 215 | r->user = r->prev->user; 216 | r->ap_auth_type = r->prev->ap_auth_type; 217 | } 218 | else if (r->main && (r->main->per_dir_config == r->per_dir_config)) { 219 | r->user = r->main->user; 220 | r->ap_auth_type = r->main->ap_auth_type; 221 | } 222 | else { 223 | switch (ap_satisfies(r)) { 224 | case SATISFY_ALL: 225 | case SATISFY_NOSPEC: 226 | if ((access_status = ap_run_access_checker(r)) != OK) { 227 | return decl_die(access_status, 228 | "check access (with Satisfy All)", r); 229 | } 230 | 231 | access_status = ap_run_access_checker_ex(r); 232 | if (access_status == OK) { 233 | ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, 234 | "request authorized without authentication by " 235 | "access_checker_ex hook: %s", r->uri); 236 | } 237 | else if (access_status != DECLINED) { 238 | return decl_die(access_status, "check access", r); 239 | } 240 | else { 241 | if ((access_status = ap_run_check_user_id(r)) != OK) { 242 | return decl_die(access_status, "check user", r); 243 | } 244 | if (r->user == NULL) { 245 | /* don't let buggy authn module crash us in authz */ 246 | ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027) 247 | "No authentication done but request not " 248 | "allowed without authentication for %s. " 249 | "Authentication not configured?", 250 | r->uri); 251 | access_status = HTTP_INTERNAL_SERVER_ERROR; 252 | return decl_die(access_status, "check user", r); 253 | } 254 | if ((access_status = ap_run_auth_checker(r)) != OK) { 255 | return decl_die(access_status, "check authorization", r); 256 | } 257 | } 258 | break; 259 | case SATISFY_ANY: 260 | if ((access_status = ap_run_access_checker(r)) == OK) { 261 | ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, 262 | "request authorized without authentication by " 263 | "access_checker hook and 'Satisfy any': %s", 264 | r->uri); 265 | break; 266 | } 267 | 268 | access_status = ap_run_access_checker_ex(r); 269 | if (access_status == OK) { 270 | ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, 271 | "request authorized without authentication by " 272 | "access_checker_ex hook: %s", r->uri); 273 | } 274 | else if (access_status != DECLINED) { 275 | return decl_die(access_status, "check access", r); 276 | } 277 | else { 278 | if ((access_status = ap_run_check_user_id(r)) != OK) { 279 | return decl_die(access_status, "check user", r); 280 | } 281 | if (r->user == NULL) { 282 | /* don't let buggy authn module crash us in authz */ 283 | ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028) 284 | "No authentication done but request not " 285 | "allowed without authentication for %s. " 286 | "Authentication not configured?", 287 | r->uri); 288 | access_status = HTTP_INTERNAL_SERVER_ERROR; 289 | return decl_die(access_status, "check user", r); 290 | } 291 | if ((access_status = ap_run_auth_checker(r)) != OK) { 292 | return decl_die(access_status, "check authorization", r); 293 | } 294 | } 295 | break; 296 | } 297 | } 298 | /* XXX Must make certain the ap_run_type_checker short circuits mime 299 | * in mod-proxy for r->proxyreq && r->parsed_uri.scheme 300 | * && !strcmp(r->parsed_uri.scheme, "http") 301 | */ 302 | if ((access_status = ap_run_type_checker(r)) != OK) { 303 | return decl_die(access_status, "find types", r); 304 | } 305 | 306 | if ((access_status = ap_run_fixups(r)) != OK) { 307 | ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "fixups hook gave %d: %s", 308 | access_status, r->uri); 309 | return access_status; 310 | } 311 | 312 | return OK; 313 | } 314 | 315 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 316 | -------------------------------------------------------------------------------- /patches/CVE-2015-0240/patches.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * Unix SMB/CIFS implementation. 5 | * RPC Pipe client / server routines 6 | * Copyright (C) Andrew Tridgell 1992-1997, 7 | * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, 8 | * Copyright (C) Paul Ashton 1997. 9 | * Copyright (C) Jeremy Allison 1998-2001. 10 | * Copyright (C) Andrew Bartlett 2001. 11 | * Copyright (C) Guenther Deschner 2008-2009. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License 24 | * along with this program; if not, see . 25 | */ 26 | 27 | /* This is the implementation of the netlogon pipe. */ 28 | 29 | #include "includes.h" 30 | #include "system/passwd.h" /* uid_wrapper */ 31 | #include "ntdomain.h" 32 | #include "../libcli/auth/schannel.h" 33 | #include "../librpc/gen_ndr/srv_netlogon.h" 34 | #include "../librpc/gen_ndr/ndr_samr_c.h" 35 | #include "../librpc/gen_ndr/ndr_lsa_c.h" 36 | #include "rpc_client/cli_lsarpc.h" 37 | #include "rpc_client/init_lsa.h" 38 | #include "rpc_server/rpc_ncacn_np.h" 39 | #include "../libcli/security/security.h" 40 | #include "../libcli/security/dom_sid.h" 41 | #include "librpc/gen_ndr/ndr_drsblobs.h" 42 | #include "lib/crypto/arcfour.h" 43 | #include "lib/crypto/md4.h" 44 | #include "nsswitch/libwbclient/wbclient.h" 45 | #include "../libcli/registry/util_reg.h" 46 | #include "passdb.h" 47 | #include "auth.h" 48 | #include "messages.h" 49 | #include "../lib/tsocket/tsocket.h" 50 | #include "lib/param/param.h" 51 | 52 | /****/ 53 | 54 | /************************************************************************* 55 | * If schannel is required for this call test that it actually is available. 56 | *************************************************************************/ 57 | 58 | static NTSTATUS samr_find_machine_account(TALLOC_CTX *mem_ctx, 59 | struct dcerpc_binding_handle *b, 60 | const char *account_name, 61 | uint32_t access_mask, 62 | struct dom_sid2 **domain_sid_p, 63 | uint32_t *user_rid_p, 64 | struct policy_handle *user_handle) 65 | { 66 | NTSTATUS status; 67 | NTSTATUS result = NT_STATUS_OK; 68 | struct policy_handle connect_handle; 69 | struct policy_handle domain_handle = { 0, }; 70 | struct lsa_String domain_name; 71 | struct dom_sid2 *domain_sid; 72 | struct lsa_String names; 73 | struct samr_Ids rids; 74 | struct samr_Ids types; 75 | uint32_t rid; 76 | 77 | status = dcerpc_samr_Connect2(b, mem_ctx, 78 | lp_netbios_name(), 79 | SAMR_ACCESS_CONNECT_TO_SERVER | 80 | SAMR_ACCESS_ENUM_DOMAINS | 81 | SAMR_ACCESS_LOOKUP_DOMAIN, 82 | &connect_handle, 83 | &result); 84 | if (!NT_STATUS_IS_OK(status)) { 85 | goto out; 86 | } 87 | if (!NT_STATUS_IS_OK(result)) { 88 | status = result; 89 | goto out; 90 | } 91 | 92 | init_lsa_String(&domain_name, get_global_sam_name()); 93 | 94 | status = dcerpc_samr_LookupDomain(b, mem_ctx, 95 | &connect_handle, 96 | &domain_name, 97 | &domain_sid, 98 | &result); 99 | if (!NT_STATUS_IS_OK(status)) { 100 | goto out; 101 | } 102 | if (!NT_STATUS_IS_OK(result)) { 103 | status = result; 104 | goto out; 105 | } 106 | 107 | status = dcerpc_samr_OpenDomain(b, mem_ctx, 108 | &connect_handle, 109 | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, 110 | domain_sid, 111 | &domain_handle, 112 | &result); 113 | if (!NT_STATUS_IS_OK(status)) { 114 | goto out; 115 | } 116 | if (!NT_STATUS_IS_OK(result)) { 117 | status = result; 118 | goto out; 119 | } 120 | 121 | init_lsa_String(&names, account_name); 122 | 123 | status = dcerpc_samr_LookupNames(b, mem_ctx, 124 | &domain_handle, 125 | 1, 126 | &names, 127 | &rids, 128 | &types, 129 | &result); 130 | if (!NT_STATUS_IS_OK(status)) { 131 | goto out; 132 | } 133 | if (!NT_STATUS_IS_OK(result)) { 134 | status = result; 135 | goto out; 136 | } 137 | 138 | if (rids.count != 1) { 139 | status = NT_STATUS_NO_SUCH_USER; 140 | goto out; 141 | } 142 | if (types.count != 1) { 143 | status = NT_STATUS_INVALID_PARAMETER; 144 | goto out; 145 | } 146 | if (types.ids[0] != SID_NAME_USER) { 147 | status = NT_STATUS_NO_SUCH_USER; 148 | goto out; 149 | } 150 | 151 | rid = rids.ids[0]; 152 | 153 | status = dcerpc_samr_OpenUser(b, mem_ctx, 154 | &domain_handle, 155 | access_mask, 156 | rid, 157 | user_handle, 158 | &result); 159 | if (!NT_STATUS_IS_OK(status)) { 160 | goto out; 161 | } 162 | if (!NT_STATUS_IS_OK(result)) { 163 | status = result; 164 | goto out; 165 | } 166 | 167 | if (user_rid_p) { 168 | *user_rid_p = rid; 169 | } 170 | 171 | if (domain_sid_p) { 172 | *domain_sid_p = domain_sid; 173 | } 174 | 175 | out: 176 | if (is_valid_policy_hnd(&domain_handle)) { 177 | dcerpc_samr_Close(b, mem_ctx, &domain_handle, &result); 178 | } 179 | if (is_valid_policy_hnd(&connect_handle)) { 180 | dcerpc_samr_Close(b, mem_ctx, &connect_handle, &result); 181 | } 182 | 183 | return status; 184 | } 185 | 186 | static NTSTATUS schannel_check_required(struct pipe_auth_data *auth_info, 187 | const char *computer_name, 188 | bool integrity, bool privacy) 189 | { 190 | if (auth_info && auth_info->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { 191 | if (!privacy && !integrity) { 192 | return NT_STATUS_OK; 193 | } 194 | 195 | if ((!privacy && integrity) && 196 | auth_info->auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { 197 | return NT_STATUS_OK; 198 | } 199 | 200 | if ((privacy || integrity) && 201 | auth_info->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { 202 | return NT_STATUS_OK; 203 | } 204 | } 205 | 206 | /* test didn't pass */ 207 | DEBUG(0, ("schannel_check_required: [%s] is not using schannel\n", 208 | computer_name)); 209 | 210 | return NT_STATUS_ACCESS_DENIED; 211 | } 212 | 213 | static NTSTATUS netr_set_machine_account_password(TALLOC_CTX *mem_ctx, 214 | struct auth_session_info *session_info, 215 | struct messaging_context *msg_ctx, 216 | const char *account_name, 217 | struct samr_Password *nt_hash) 218 | { 219 | NTSTATUS status; 220 | NTSTATUS result = NT_STATUS_OK; 221 | struct dcerpc_binding_handle *h = NULL; 222 | struct tsocket_address *local; 223 | struct policy_handle user_handle; 224 | uint32_t acct_ctrl; 225 | union samr_UserInfo *info; 226 | struct samr_UserInfo18 info18; 227 | DATA_BLOB in,out; 228 | int rc; 229 | DATA_BLOB session_key; 230 | 231 | ZERO_STRUCT(user_handle); 232 | 233 | status = session_extract_session_key(session_info, 234 | &session_key, 235 | KEY_USE_16BYTES); 236 | if (!NT_STATUS_IS_OK(status)) { 237 | goto out; 238 | } 239 | 240 | rc = tsocket_address_inet_from_strings(mem_ctx, 241 | "ip", 242 | "127.0.0.1", 243 | 0, 244 | &local); 245 | if (rc < 0) { 246 | status = NT_STATUS_NO_MEMORY; 247 | goto out; 248 | } 249 | 250 | status = rpcint_binding_handle(mem_ctx, 251 | &ndr_table_samr, 252 | local, 253 | session_info, 254 | msg_ctx, 255 | &h); 256 | if (!NT_STATUS_IS_OK(status)) { 257 | goto out; 258 | } 259 | 260 | become_root(); 261 | status = samr_find_machine_account(mem_ctx, 262 | h, 263 | account_name, 264 | SEC_FLAG_MAXIMUM_ALLOWED, 265 | NULL, 266 | NULL, 267 | &user_handle); 268 | unbecome_root(); 269 | if (!NT_STATUS_IS_OK(status)) { 270 | goto out; 271 | } 272 | 273 | status = dcerpc_samr_QueryUserInfo2(h, 274 | mem_ctx, 275 | &user_handle, 276 | UserControlInformation, 277 | &info, 278 | &result); 279 | if (!NT_STATUS_IS_OK(status)) { 280 | goto out; 281 | } 282 | if (!NT_STATUS_IS_OK(result)) { 283 | status = result; 284 | goto out; 285 | } 286 | 287 | acct_ctrl = info->info16.acct_flags; 288 | 289 | if (!(acct_ctrl & ACB_WSTRUST || 290 | acct_ctrl & ACB_SVRTRUST || 291 | acct_ctrl & ACB_DOMTRUST)) { 292 | status = NT_STATUS_NO_SUCH_USER; 293 | goto out; 294 | } 295 | 296 | if (acct_ctrl & ACB_DISABLED) { 297 | status = NT_STATUS_ACCOUNT_DISABLED; 298 | goto out; 299 | } 300 | 301 | ZERO_STRUCT(info18); 302 | 303 | in = data_blob_const(nt_hash->hash, 16); 304 | out = data_blob_talloc_zero(mem_ctx, 16); 305 | sess_crypt_blob(&out, &in, &session_key, true); 306 | memcpy(info18.nt_pwd.hash, out.data, out.length); 307 | 308 | info18.nt_pwd_active = true; 309 | 310 | info->info18 = info18; 311 | 312 | become_root(); 313 | status = dcerpc_samr_SetUserInfo2(h, 314 | mem_ctx, 315 | &user_handle, 316 | UserInternal1Information, 317 | info, 318 | &result); 319 | unbecome_root(); 320 | if (!NT_STATUS_IS_OK(status)) { 321 | goto out; 322 | } 323 | if (!NT_STATUS_IS_OK(result)) { 324 | status = result; 325 | goto out; 326 | } 327 | 328 | out: 329 | if (h && is_valid_policy_hnd(&user_handle)) { 330 | dcerpc_samr_Close(h, mem_ctx, &user_handle, &result); 331 | } 332 | 333 | return status; 334 | } 335 | 336 | /****/ 337 | 338 | NTSTATUS patched_netr_creds_server_step_check(struct pipes_struct *p, 339 | TALLOC_CTX *mem_ctx, 340 | const char *computer_name, 341 | struct netr_Authenticator *received_authenticator, 342 | struct netr_Authenticator *return_authenticator, 343 | struct netlogon_creds_CredentialState **creds_out) 344 | { 345 | NTSTATUS status; 346 | bool schannel_global_required = (lp_server_schannel() == true) ? true:false; 347 | struct loadparm_context *lp_ctx; 348 | 349 | sd_journal_print(LOG_INFO, "%s: %d\n", __func__, __LINE__); 350 | 351 | if (schannel_global_required) { 352 | status = schannel_check_required(&p->auth, 353 | computer_name, 354 | false, false); 355 | if (!NT_STATUS_IS_OK(status)) { 356 | return status; 357 | } 358 | } 359 | 360 | lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers()); 361 | if (lp_ctx == NULL) { 362 | DEBUG(0, ("loadparm_init_s3 failed\n")); 363 | return NT_STATUS_INTERNAL_ERROR; 364 | } 365 | 366 | status = schannel_check_creds_state(mem_ctx, lp_ctx, 367 | computer_name, received_authenticator, 368 | return_authenticator, creds_out); 369 | talloc_unlink(mem_ctx, lp_ctx); 370 | return status; 371 | } 372 | 373 | NTSTATUS patched__netr_ServerPasswordSet(struct pipes_struct *p, 374 | struct netr_ServerPasswordSet *r) 375 | { 376 | NTSTATUS status = NT_STATUS_OK; 377 | int i; 378 | struct netlogon_creds_CredentialState *creds; 379 | 380 | sd_journal_print(LOG_INFO, "%s: %d\n", __func__, __LINE__); 381 | 382 | DEBUG(5,("_netr_ServerPasswordSet: %d\n", __LINE__)); 383 | 384 | become_root(); 385 | status = patched_netr_creds_server_step_check(p, p->mem_ctx, 386 | r->in.computer_name, 387 | r->in.credential, 388 | r->out.return_authenticator, 389 | &creds); 390 | unbecome_root(); 391 | 392 | if (!NT_STATUS_IS_OK(status)) { 393 | DEBUG(2,("_netr_ServerPasswordSet: netlogon_creds_server_step failed. Rejecting auth " 394 | "request from client %s machine account %s\n", 395 | r->in.computer_name, creds->computer_name)); 396 | TALLOC_FREE(creds); 397 | return status; 398 | } 399 | 400 | DEBUG(3,("_netr_ServerPasswordSet: Server Password Set by remote machine:[%s] on account [%s]\n", 401 | r->in.computer_name, creds->computer_name)); 402 | 403 | netlogon_creds_des_decrypt(creds, r->in.new_password); 404 | 405 | DEBUG(100,("_netr_ServerPasswordSet: new given value was :\n")); 406 | for(i = 0; i < sizeof(r->in.new_password->hash); i++) 407 | DEBUG(100,("%02X ", r->in.new_password->hash[i])); 408 | DEBUG(100,("\n")); 409 | 410 | status = netr_set_machine_account_password(p->mem_ctx, 411 | p->session_info, 412 | p->msg_ctx, 413 | creds->account_name, 414 | r->in.new_password); 415 | return status; 416 | } 417 | 418 | 419 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 420 | -------------------------------------------------------------------------------- /inject-arm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | #include "ptrace.h" 10 | 11 | /* 12 | * injectSharedLibrary() 13 | * 14 | * This is the code that will actually be injected into the target process. 15 | * This code is responsible for loading the shared library into the target 16 | * process' address space. First, it calls malloc() to allocate a buffer to 17 | * hold the filename of the library to be loaded. Then, it calls 18 | * __libc_dlopen_mode(), libc's implementation of dlopen(), to load the desired 19 | * shared library. Finally, it calls free() to free the buffer containing the 20 | * library name. Each time it needs to give control back to the injector 21 | * process, it breaks back in by calling raise() to produce a SIGTRAP signal. 22 | * See the comments below for more details on how this works. 23 | * 24 | */ 25 | 26 | void injectSharedLibrary(long mallocaddr, long freeaddr, long dlopenaddr) 27 | { 28 | // r1 = address of raise() 29 | // r2 = address of malloc() 30 | // r3 = address of __libc_dlopen_mode() 31 | // r4 = address of free() 32 | // r5 = size of the path to the shared library we want to load 33 | // 34 | // unfortunately, each function call we make will wipe out these 35 | // register values, so in order to avoid losing the function addresses, 36 | // we need to save them on the stack. 37 | // 38 | // here's the sequence of calls we're going to make: 39 | // 40 | // * malloc() - allocate a buffer to store the path to the shared 41 | // library we're injecting 42 | // 43 | // * raise() - raise a SIGTRAP signal to break into the target process 44 | // so that we can check the return value of malloc() in order to know 45 | // where to copy the shared library path to 46 | // 47 | // * __libc_dlopen_mode() - load the shared library 48 | // 49 | // * raise() - raise a SIGTRAP signal to break into the target process 50 | // to check the return value of __libc_dlopen_mode() in order to see 51 | // whether it succeeded 52 | // 53 | // * free() - free the buffer containing the path to the shared library 54 | // 55 | // * raise() - raise a SIGTRAP signal to break into the target process 56 | // so that we can restore the parts of memory that we overwrote 57 | // 58 | // we need to push the addresses of the functions we want to call in 59 | // the reverse of the order we want to call them in (except for the 60 | // first call): 61 | 62 | asm("push {r1}"); // raise() 63 | asm("push {r4}"); // free() 64 | asm("push {r1}"); // raise() 65 | asm("push {r3}"); // __libc_dlopen_mode() 66 | asm("push {r1}"); // raise() 67 | 68 | // call malloc() to allocate a buffer to store the path to the shared 69 | // library to inject. 70 | asm( 71 | // choose the amount of memory to allocate with malloc() based 72 | // on the size of the path to the shared library passed via r5 73 | "mov r0, r5 \n" 74 | // call malloc(), whose address is already in r2 75 | "blx r2 \n" 76 | // copy the return value (which is in r0) into r5 so that it 77 | // doesn't get wiped out later 78 | "mov r5, r0" 79 | ); 80 | 81 | // call raise(SIGTRAP) to get back control of the target. 82 | asm( 83 | // pop off the stack to get the address of raise() 84 | "pop {r1} \n" 85 | // specify SIGTRAP as the first argument 86 | "mov r0, #5 \n" 87 | // call raise() 88 | "blx r1" 89 | ); 90 | 91 | // call __libc_dlopen_mode() to actually load the shared library. 92 | asm( 93 | // pop off the stack to get the address of __libc_dlopen_mode() 94 | "pop {r2} \n" 95 | // copy r5 (the address of the malloc'd buffer) into r0 to make 96 | // it the first argument to __libc_dlopen_mode() 97 | "mov r0, r5 \n" 98 | // set the second argument to RTLD_LAZY 99 | "mov r1, #1 \n" 100 | // call __libc_dlopen_mode() 101 | "blx r2 \n" 102 | // copy the return value (which is in r0) into r4 so that it 103 | // doesn't get wiped out later 104 | "mov r4, r0" 105 | ); 106 | 107 | // call raise(SIGTRAP) to get back control of the target. 108 | asm( 109 | // pop off the stack to get the address of raise() 110 | "pop {r1} \n" 111 | // specify SIGTRAP as the first argument 112 | "mov r0, #5 \n" 113 | // call raise() 114 | "blx r1" 115 | ); 116 | 117 | // call free() in order to free the buffer containing the path to the 118 | // shared library. 119 | asm( 120 | // pop off the stack to get the address of free() 121 | "pop {r2} \n" 122 | // copy r5 (the malloc'd buffer) into r0 to make it the first 123 | // argument to free() 124 | "mov r0, r5 \n" 125 | // call __libc_dlopen_mode() 126 | "blx r2 \n" 127 | // copy return value r0 into r4 so that it doesn't get wiped 128 | // out later 129 | "mov r4, r0" 130 | ); 131 | 132 | // call raise(SIGTRAP) to get back control of the target. 133 | asm( 134 | // pop off the stack to get the address of raise() 135 | "pop {r1} \n" 136 | // specify SIGTRAP as the first argument 137 | "mov r0, #5 \n" 138 | // call raise() 139 | "blx r1" 140 | ); 141 | } 142 | 143 | /* 144 | * injectSharedLibrary_end() 145 | * 146 | * This function's only purpose is to be contiguous to injectSharedLibrary(), 147 | * so that we can use its address to more precisely figure out how long 148 | * injectSharedLibrary() is. 149 | * 150 | */ 151 | 152 | void injectSharedLibrary_end() 153 | { 154 | } 155 | 156 | int main(int argc, char** argv) 157 | { 158 | if(argc < 4) 159 | { 160 | usage(argv[0]); 161 | return 1; 162 | } 163 | 164 | char* command = argv[1]; 165 | char* commandArg = argv[2]; 166 | char* libname = argv[3]; 167 | char* libPath = realpath(libname, NULL); 168 | 169 | char* processName = NULL; 170 | pid_t target = 0; 171 | 172 | if(!libPath) 173 | { 174 | fprintf(stderr, "can't find file \"%s\"\n", libname); 175 | return 1; 176 | } 177 | 178 | if(!strcmp(command, "-n")) 179 | { 180 | processName = commandArg; 181 | target = findProcessByName(processName); 182 | if(target == -1) 183 | { 184 | fprintf(stderr, "doesn't look like a process named \"%s\" is running right now\n", processName); 185 | return 1; 186 | } 187 | 188 | printf("targeting process \"%s\" with pid %d\n", processName, target); 189 | } 190 | else if(!strcmp(command, "-p")) 191 | { 192 | target = atoi(commandArg); 193 | printf("targeting process with pid %d\n", target); 194 | } 195 | else 196 | { 197 | usage(argv[0]); 198 | return 1; 199 | } 200 | 201 | int libPathLength = strlen(libPath) + 1; 202 | 203 | int mypid = getpid(); 204 | long mylibcaddr = getlibcaddr(mypid); 205 | 206 | // find the addresses of the syscalls that we'd like to use inside the 207 | // target, as loaded inside THIS process (i.e. NOT the target process) 208 | long mallocAddr = getFunctionAddress("malloc"); 209 | long freeAddr = getFunctionAddress("free"); 210 | long dlopenAddr = getFunctionAddress("__libc_dlopen_mode"); 211 | long raiseAddr = getFunctionAddress("raise"); 212 | 213 | // use the base address of libc to calculate offsets for the syscalls 214 | // we want to use 215 | long mallocOffset = mallocAddr - mylibcaddr; 216 | long freeOffset = freeAddr - mylibcaddr; 217 | long dlopenOffset = dlopenAddr - mylibcaddr; 218 | long raiseOffset = raiseAddr - mylibcaddr; 219 | 220 | // get the target process' libc address and use it to find the 221 | // addresses of the syscalls we want to use inside the target process 222 | long targetLibcAddr = getlibcaddr(target); 223 | long targetMallocAddr = targetLibcAddr + mallocOffset; 224 | long targetFreeAddr = targetLibcAddr + freeOffset; 225 | long targetDlopenAddr = targetLibcAddr + dlopenOffset; 226 | long targetRaiseAddr = targetLibcAddr + raiseOffset; 227 | 228 | struct user_regs oldregs, regs; 229 | memset(&oldregs, 0, sizeof(struct user_regs)); 230 | memset(®s, 0, sizeof(struct user_regs)); 231 | 232 | ptrace_attach(target); 233 | 234 | ptrace_getregs(target, &oldregs); 235 | memcpy(®s, &oldregs, sizeof(struct user_regs)); 236 | 237 | // find a good address to copy code to 238 | long addr = freespaceaddr(target) + sizeof(long); 239 | 240 | // now that we have an address to copy code to, set the target's 241 | // program counter to it. 242 | // 243 | // subtract 4 bytes from the actual address, because ARM's PC actually 244 | // refers to the next instruction rather than the current instruction. 245 | regs.uregs[15] = addr - 4; 246 | 247 | // pass arguments to my function injectSharedLibrary() by loading them 248 | // into the right registers. see comments in injectSharedLibrary() for 249 | // more details. 250 | regs.uregs[1] = targetRaiseAddr; 251 | regs.uregs[2] = targetMallocAddr; 252 | regs.uregs[3] = targetDlopenAddr; 253 | regs.uregs[4] = targetFreeAddr; 254 | regs.uregs[5] = libPathLength; 255 | ptrace_setregs(target, ®s); 256 | 257 | // figure out the size of injectSharedLibrary() so we know how big of a buffer to allocate. 258 | size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)injectSharedLibrary; 259 | 260 | // back up whatever data used to be at the address we want to modify. 261 | char* backup = malloc(injectSharedLibrary_size * sizeof(char)); 262 | ptrace_read(target, addr, backup, injectSharedLibrary_size); 263 | 264 | // set up a buffer containing the code that we'll inject into the target process. 265 | char* newcode = malloc(injectSharedLibrary_size * sizeof(char)); 266 | memset(newcode, 0, injectSharedLibrary_size * sizeof(char)); 267 | 268 | // copy the code of injectSharedLibrary() to the buffer. 269 | memcpy(newcode, injectSharedLibrary, injectSharedLibrary_size); 270 | 271 | // copy injectSharedLibrary()'s code to the target address inside the 272 | // target process' address space. 273 | ptrace_write(target, addr, newcode, injectSharedLibrary_size); 274 | 275 | // now that the new code is in place, let the target run our injected code. 276 | ptrace_cont(target); 277 | 278 | // at this point, the target should have run malloc(). check its return 279 | // value to see if it succeeded, and bail out cleanly if it didn't. 280 | struct user_regs malloc_regs; 281 | memset(&malloc_regs, 0, sizeof(struct user_regs)); 282 | ptrace_getregs(target, &malloc_regs); 283 | unsigned long long targetBuf = malloc_regs.uregs[5]; 284 | 285 | // if r5 is 0 here, then malloc failed, and we should bail out cleanly. 286 | if(targetBuf == 0) 287 | { 288 | fprintf(stderr, "malloc() failed to allocate memory\n"); 289 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 290 | free(backup); 291 | free(newcode); 292 | return 1; 293 | } 294 | 295 | // if we get here, then malloc likely succeeded, so now we need to copy 296 | // the path to the shared library we want to inject into the buffer 297 | // that the target process just malloc'd. this is needed so that it can 298 | // be passed as an argument to __libc_dlopen_mode later on. 299 | 300 | // read the buffer returned by malloc() and copy the name of our shared 301 | // library to that address inside the target process. 302 | ptrace_write(target, targetBuf, libPath, libPathLength); 303 | 304 | // continue the target's execution again in order to call 305 | // __libc_dlopen_mode. 306 | ptrace_cont(target); 307 | 308 | // check out what the registers look like after calling 309 | // __libc_dlopen_mode. 310 | struct user_regs dlopen_regs; 311 | memset(&dlopen_regs, 0, sizeof(struct user_regs)); 312 | ptrace_getregs(target, &dlopen_regs); 313 | unsigned long long libAddr = dlopen_regs.uregs[4]; 314 | 315 | // if r4 is 0 here, then __libc_dlopen_mode() failed, and we should 316 | // bail out cleanly. 317 | if(libAddr == 0) 318 | { 319 | fprintf(stderr, "__libc_dlopen_mode() failed to load %s\n", libname); 320 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 321 | free(backup); 322 | free(newcode); 323 | return 1; 324 | } 325 | 326 | // now check /proc/pid/maps to see whether injection was successful. 327 | if(checkloaded(target, libname)) 328 | { 329 | printf("\"%s\" successfully injected\n", libname); 330 | } 331 | else 332 | { 333 | fprintf(stderr, "could not inject \"%s\"\n", libname); 334 | } 335 | 336 | // as a courtesy, free the buffer that we allocated inside the target 337 | // process. we don't really care whether this succeeds, so don't 338 | // bother checking the return value. 339 | ptrace_cont(target); 340 | 341 | // at this point, if everything went according to plan, we've loaded 342 | // the shared library inside the target process, so we're done. restore 343 | // the old state and detach from the target. 344 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 345 | free(backup); 346 | free(newcode); 347 | return 0; 348 | } 349 | -------------------------------------------------------------------------------- /inject-x86_64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "utils.h" 10 | #include "ptrace.h" 11 | 12 | /* 13 | * injectSharedLibrary() 14 | * 15 | * This is the code that will actually be injected into the target process. 16 | * This code is responsible for loading the shared library into the target 17 | * process' address space. First, it calls malloc() to allocate a buffer to 18 | * hold the filename of the library to be loaded. Then, it calls 19 | * __libc_dlopen_mode(), libc's implementation of dlopen(), to load the desired 20 | * shared library. Finally, it calls free() to free the buffer containing the 21 | * library name. Each time it needs to give control back to the injector 22 | * process, it breaks back in by executing an "int $3" instruction. See the 23 | * comments below for more details on how this works. 24 | * 25 | */ 26 | 27 | void injectSharedLibrary(long mallocaddr, long freeaddr, long dlopenaddr) 28 | { 29 | // here are the assumptions I'm making about what data will be located 30 | // where at the time the target executes this code: 31 | // 32 | // rdi = address of malloc() in target process 33 | // rsi = address of free() in target process 34 | // rdx = address of __libc_dlopen_mode() in target process 35 | // rcx = size of the path to the shared library we want to load 36 | 37 | // save addresses of free() and __libc_dlopen_mode() on the stack for later use 38 | asm( 39 | // rsi is going to contain the address of free(). it's going to get wiped 40 | // out by the call to malloc(), so save it on the stack for later 41 | "push %rsi \n" 42 | // same thing for rdx, which will contain the address of _dl_open() 43 | "push %rdx" 44 | ); 45 | 46 | // call malloc() from within the target process 47 | asm( 48 | // save previous value of r9, because we're going to use it to call malloc() 49 | "push %r9 \n" 50 | // now move the address of malloc() into r9 51 | "mov %rdi,%r9 \n" 52 | // choose the amount of memory to allocate with malloc() based on the size 53 | // of the path to the shared library passed via rcx 54 | "mov %rcx,%rdi \n" 55 | // now call r9; malloc() 56 | "callq *%r9 \n" 57 | // after returning from malloc(), pop the previous value of r9 off the stack 58 | "pop %r9 \n" 59 | // break in so that we can see what malloc() returned 60 | "int $3" 61 | ); 62 | 63 | // call __libc_dlopen_mode() to load the shared library 64 | asm( 65 | // get the address of __libc_dlopen_mode() off of the stack so we can call it 66 | "pop %rdx \n" 67 | // as before, save the previous value of r9 on the stack 68 | "push %r9 \n" 69 | // copy the address of __libc_dlopen_mode() into r9 70 | "mov %rdx,%r9 \n" 71 | // 1st argument to __libc_dlopen_mode(): filename = the address of the buffer returned by malloc() 72 | "mov %rax,%rdi \n" 73 | // 2nd argument to __libc_dlopen_mode(): flag = RTLD_LAZY 74 | "movabs $1,%rsi \n" 75 | // call __libc_dlopen_mode() 76 | "callq *%r9 \n" 77 | // restore old r9 value 78 | "pop %r9 \n" 79 | // break in so that we can see what __libc_dlopen_mode() returned 80 | "int $3" 81 | ); 82 | 83 | // call free() to free the buffer we allocated earlier. 84 | // 85 | // Note: I found that if you put a nonzero value in r9, free() seems to 86 | // interpret that as an address to be freed, even though it's only 87 | // supposed to take one argument. As a result, I had to call it using a 88 | // register that's not used as part of the x64 calling convention. I 89 | // chose rbx. 90 | asm( 91 | // at this point, rax should still contain our malloc()d buffer from earlier. 92 | // we're going to free it, so move rax into rdi to make it the first argument to free(). 93 | "mov %rax,%rdi \n" 94 | // pop rsi so that we can get the address to free(), which we pushed onto the stack a while ago. 95 | "pop %rsi \n" 96 | // save previous rbx value 97 | "push %rbx \n" 98 | // load the address of free() into rbx 99 | "mov %rsi,%rbx \n" 100 | // zero out rsi, because free() might think that it contains something that should be freed 101 | "xor %rsi,%rsi \n" 102 | // break in so that we can check out the arguments right before making the call 103 | "int $3 \n" 104 | // call free() 105 | "callq *%rbx \n" 106 | // restore previous rbx value 107 | "pop %rbx" 108 | ); 109 | 110 | // we already overwrote the RET instruction at the end of this function 111 | // with an INT 3, so at this point the injector will regain control of 112 | // the target's execution. 113 | } 114 | 115 | /* 116 | * injectSharedLibrary_end() 117 | * 118 | * This function's only purpose is to be contiguous to injectSharedLibrary(), 119 | * so that we can use its address to more precisely figure out how long 120 | * injectSharedLibrary() is. 121 | * 122 | */ 123 | 124 | void injectSharedLibrary_end() 125 | { 126 | } 127 | 128 | int main(int argc, char** argv) 129 | { 130 | if(argc < 4) 131 | { 132 | usage(argv[0]); 133 | return 1; 134 | } 135 | 136 | char* command = argv[1]; 137 | char* commandArg = argv[2]; 138 | char* libname = argv[3]; 139 | char* libPath = realpath(libname, NULL); 140 | 141 | char* processName = NULL; 142 | pid_t target = 0; 143 | 144 | if(!libPath) 145 | { 146 | fprintf(stderr, "can't find file \"%s\"\n", libname); 147 | return 1; 148 | } 149 | 150 | if(!strcmp(command, "-d")) 151 | { 152 | void *handle; 153 | int i; 154 | 155 | for (i=4; i 68 | #include 69 | 70 | #include 71 | #include 72 | 73 | #include 74 | 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | 81 | /* 82 | * Check whether "cp" is a valid ascii representation 83 | * of an Internet address and convert to a binary address. 84 | * Returns 1 if the address is valid, 0 if not. 85 | * This replaces inet_addr, the return value from which 86 | * cannot distinguish between failure and a local broadcast address. 87 | */ 88 | int 89 | private__inet_aton(const char *cp, struct in_addr *addr) 90 | { 91 | static const in_addr_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff }; 92 | in_addr_t val; 93 | char c; 94 | union iaddr { 95 | uint8_t bytes[4]; 96 | uint32_t word; 97 | } res; 98 | uint8_t *pp = res.bytes; 99 | int digit; 100 | 101 | int saved_errno = errno; 102 | __set_errno (0); 103 | 104 | res.word = 0; 105 | 106 | c = *cp; 107 | for (;;) { 108 | /* 109 | * Collect number up to ``.''. 110 | * Values are specified as for C: 111 | * 0x=hex, 0=octal, isdigit=decimal. 112 | */ 113 | if (!isdigit(c)) 114 | goto ret_0; 115 | { 116 | char *endp; 117 | unsigned long ul = strtoul (cp, (char **) &endp, 0); 118 | if (ul == ULONG_MAX && errno == ERANGE) 119 | goto ret_0; 120 | if (ul > 0xfffffffful) 121 | goto ret_0; 122 | val = ul; 123 | digit = cp != endp; 124 | cp = endp; 125 | } 126 | c = *cp; 127 | if (c == '.') { 128 | /* 129 | * Internet format: 130 | * a.b.c.d 131 | * a.b.c (with c treated as 16 bits) 132 | * a.b (with b treated as 24 bits) 133 | */ 134 | if (pp > res.bytes + 2 || val > 0xff) 135 | goto ret_0; 136 | *pp++ = val; 137 | c = *++cp; 138 | } else 139 | break; 140 | } 141 | /* 142 | * Check for trailing characters. 143 | */ 144 | if (c != '\0' && (!isascii(c) || !isspace(c))) 145 | goto ret_0; 146 | /* 147 | * Did we get a valid digit? 148 | */ 149 | if (!digit) 150 | goto ret_0; 151 | 152 | /* Check whether the last part is in its limits depending on 153 | the number of parts in total. */ 154 | if (val > max[pp - res.bytes]) 155 | goto ret_0; 156 | 157 | if (addr != NULL) 158 | addr->s_addr = res.word | htonl (val); 159 | 160 | __set_errno (saved_errno); 161 | return (1); 162 | 163 | ret_0: 164 | __set_errno (saved_errno); 165 | return (0); 166 | } 167 | weak_alias (private__inet_aton, inet_aton) 168 | libc_hidden_def (private__inet_aton) 169 | libc_hidden_weak (inet_aton) 170 | 171 | /* Copyright (C) 1997-2013 Free Software Foundation, Inc. 172 | This file is part of the GNU C Library. 173 | Contributed by H.J. Lu , 1997. 174 | 175 | The GNU C Library is free software; you can redistribute it and/or 176 | modify it under the terms of the GNU Lesser General Public 177 | License as published by the Free Software Foundation; either 178 | version 2.1 of the License, or (at your option) any later version. 179 | 180 | The GNU C Library is distributed in the hope that it will be useful, 181 | but WITHOUT ANY WARRANTY; without even the implied warranty of 182 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 183 | Lesser General Public License for more details. 184 | 185 | You should have received a copy of the GNU Lesser General Public 186 | License along with the GNU C Library; if not, see 187 | . */ 188 | 189 | #include 190 | #include 191 | #include 192 | #include 193 | #include 194 | #include 195 | #include 196 | #include 197 | #include 198 | #include "nsswitch.h" 199 | 200 | #ifdef USE_NSCD 201 | # define inet_aton private__inet_aton 202 | # include 203 | #endif 204 | 205 | int 206 | patched___nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 207 | char **buffer, size_t *buffer_size, 208 | size_t buflen, struct hostent **result, 209 | enum nss_status *status, int af, int *h_errnop) 210 | { 211 | int save; 212 | 213 | /* We have to test for the use of IPv6 which can only be done by 214 | examining `_res'. */ 215 | if (__res_maybe_init (&_res, 0) == -1) 216 | { 217 | if (h_errnop) 218 | *h_errnop = NETDB_INTERNAL; 219 | *result = NULL; 220 | return -1; 221 | } 222 | 223 | /* 224 | * disallow names consisting only of digits/dots, unless 225 | * they end in a dot. 226 | */ 227 | if (isdigit (name[0]) || isxdigit (name[0]) || name[0] == ':') 228 | { 229 | const char *cp; 230 | char *hostname; 231 | typedef unsigned char host_addr_t[16]; 232 | host_addr_t *host_addr; 233 | typedef char *host_addr_list_t[2]; 234 | host_addr_list_t *h_addr_ptrs; 235 | char **h_alias_ptr; 236 | size_t size_needed; 237 | int addr_size; 238 | 239 | switch (af) 240 | { 241 | case AF_INET: 242 | addr_size = INADDRSZ; 243 | break; 244 | 245 | case AF_INET6: 246 | addr_size = IN6ADDRSZ; 247 | break; 248 | 249 | default: 250 | af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET; 251 | addr_size = af == AF_INET6 ? IN6ADDRSZ : INADDRSZ; 252 | break; 253 | } 254 | 255 | size_needed = (sizeof (*host_addr) 256 | + sizeof (*h_addr_ptrs) + strlen (name) + 1); 257 | 258 | if (buffer_size == NULL) 259 | { 260 | if (buflen < size_needed) 261 | { 262 | if (h_errnop != NULL) 263 | *h_errnop = TRY_AGAIN; 264 | __set_errno (ERANGE); 265 | goto done; 266 | } 267 | } 268 | else if (buffer_size != NULL && *buffer_size < size_needed) 269 | { 270 | char *new_buf; 271 | *buffer_size = size_needed; 272 | new_buf = (char *) realloc (*buffer, *buffer_size); 273 | 274 | if (new_buf == NULL) 275 | { 276 | save = errno; 277 | free (*buffer); 278 | *buffer = NULL; 279 | *buffer_size = 0; 280 | __set_errno (save); 281 | if (h_errnop != NULL) 282 | *h_errnop = TRY_AGAIN; 283 | *result = NULL; 284 | goto done; 285 | } 286 | *buffer = new_buf; 287 | } 288 | 289 | memset (*buffer, '\0', size_needed); 290 | 291 | host_addr = (host_addr_t *) *buffer; 292 | h_addr_ptrs = (host_addr_list_t *) 293 | ((char *) host_addr + sizeof (*host_addr)); 294 | h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs)); 295 | hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr); 296 | 297 | if (isdigit (name[0])) 298 | { 299 | for (cp = name;; ++cp) 300 | { 301 | if (*cp == '\0') 302 | { 303 | int ok; 304 | 305 | if (*--cp == '.') 306 | break; 307 | 308 | /* All-numeric, no dot at the end. Fake up a hostent as if 309 | we'd actually done a lookup. What if someone types 310 | 255.255.255.255? The test below will succeed 311 | spuriously... ??? */ 312 | if (af == AF_INET) 313 | ok = private__inet_aton (name, (struct in_addr *) host_addr); 314 | else 315 | { 316 | assert (af == AF_INET6); 317 | ok = inet_pton (af, name, host_addr) > 0; 318 | } 319 | if (! ok) 320 | { 321 | *h_errnop = HOST_NOT_FOUND; 322 | if (buffer_size) 323 | *result = NULL; 324 | goto done; 325 | } 326 | 327 | resbuf->h_name = strcpy (hostname, name); 328 | h_alias_ptr[0] = NULL; 329 | resbuf->h_aliases = h_alias_ptr; 330 | (*h_addr_ptrs)[0] = (char *) host_addr; 331 | (*h_addr_ptrs)[1] = NULL; 332 | resbuf->h_addr_list = *h_addr_ptrs; 333 | if (af == AF_INET && (_res.options & RES_USE_INET6)) 334 | { 335 | /* We need to change the IP v4 address into the 336 | IP v6 address. */ 337 | char tmp[INADDRSZ]; 338 | char *p = (char *) host_addr; 339 | int i; 340 | 341 | /* Save a copy of the IP v4 address. */ 342 | memcpy (tmp, host_addr, INADDRSZ); 343 | /* Mark this ipv6 addr as a mapped ipv4. */ 344 | for (i = 0; i < 10; i++) 345 | *p++ = 0x00; 346 | *p++ = 0xff; 347 | *p++ = 0xff; 348 | /* Copy the IP v4 address. */ 349 | memcpy (p, tmp, INADDRSZ); 350 | resbuf->h_addrtype = AF_INET6; 351 | resbuf->h_length = IN6ADDRSZ; 352 | } 353 | else 354 | { 355 | resbuf->h_addrtype = af; 356 | resbuf->h_length = addr_size; 357 | } 358 | if (h_errnop != NULL) 359 | *h_errnop = NETDB_SUCCESS; 360 | if (buffer_size == NULL) 361 | *status = NSS_STATUS_SUCCESS; 362 | else 363 | *result = resbuf; 364 | goto done; 365 | } 366 | 367 | if (!isdigit (*cp) && *cp != '.') 368 | break; 369 | } 370 | } 371 | 372 | if ((isxdigit (name[0]) && strchr (name, ':') != NULL) || name[0] == ':') 373 | { 374 | const char *cp; 375 | char *hostname; 376 | typedef unsigned char host_addr_t[16]; 377 | host_addr_t *host_addr; 378 | typedef char *host_addr_list_t[2]; 379 | host_addr_list_t *h_addr_ptrs; 380 | size_t size_needed; 381 | int addr_size; 382 | 383 | switch (af) 384 | { 385 | default: 386 | af = (_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET; 387 | if (af == AF_INET6) 388 | { 389 | addr_size = IN6ADDRSZ; 390 | break; 391 | } 392 | /* FALLTHROUGH */ 393 | 394 | case AF_INET: 395 | /* This is not possible. We cannot represent an IPv6 address 396 | in an `struct in_addr' variable. */ 397 | *h_errnop = HOST_NOT_FOUND; 398 | *result = NULL; 399 | goto done; 400 | 401 | case AF_INET6: 402 | addr_size = IN6ADDRSZ; 403 | break; 404 | } 405 | 406 | size_needed = (sizeof (*host_addr) 407 | + sizeof (*h_addr_ptrs) + strlen (name) + 1); 408 | 409 | if (buffer_size == NULL && buflen < size_needed) 410 | { 411 | if (h_errnop != NULL) 412 | *h_errnop = TRY_AGAIN; 413 | __set_errno (ERANGE); 414 | goto done; 415 | } 416 | else if (buffer_size != NULL && *buffer_size < size_needed) 417 | { 418 | char *new_buf; 419 | *buffer_size = size_needed; 420 | new_buf = realloc (*buffer, *buffer_size); 421 | 422 | if (new_buf == NULL) 423 | { 424 | save = errno; 425 | free (*buffer); 426 | __set_errno (save); 427 | *buffer = NULL; 428 | *buffer_size = 0; 429 | *result = NULL; 430 | goto done; 431 | } 432 | *buffer = new_buf; 433 | } 434 | 435 | memset (*buffer, '\0', size_needed); 436 | 437 | host_addr = (host_addr_t *) *buffer; 438 | h_addr_ptrs = (host_addr_list_t *) 439 | ((char *) host_addr + sizeof (*host_addr)); 440 | hostname = (char *) h_addr_ptrs + sizeof (*h_addr_ptrs); 441 | 442 | for (cp = name;; ++cp) 443 | { 444 | if (!*cp) 445 | { 446 | if (*--cp == '.') 447 | break; 448 | 449 | /* All-IPv6-legal, no dot at the end. Fake up a 450 | hostent as if we'd actually done a lookup. */ 451 | if (inet_pton (AF_INET6, name, host_addr) <= 0) 452 | { 453 | *h_errnop = HOST_NOT_FOUND; 454 | if (buffer_size) 455 | *result = NULL; 456 | goto done; 457 | } 458 | 459 | resbuf->h_name = strcpy (hostname, name); 460 | h_alias_ptr[0] = NULL; 461 | resbuf->h_aliases = h_alias_ptr; 462 | (*h_addr_ptrs)[0] = (char *) host_addr; 463 | (*h_addr_ptrs)[1] = (char *) 0; 464 | resbuf->h_addr_list = *h_addr_ptrs; 465 | resbuf->h_addrtype = AF_INET6; 466 | resbuf->h_length = addr_size; 467 | *h_errnop = NETDB_SUCCESS; 468 | if (buffer_size == NULL) 469 | *status = NSS_STATUS_SUCCESS; 470 | else 471 | *result = resbuf; 472 | goto done; 473 | } 474 | 475 | if (!isxdigit (*cp) && *cp != ':' && *cp != '.') 476 | break; 477 | } 478 | } 479 | } 480 | 481 | return 0; 482 | 483 | done: 484 | return 1; 485 | } 486 | libc_hidden_def (__nss_hostname_digits_dots) 487 | 488 | 489 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 490 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | -------------------------------------------------------------------------------- /patches/CVE-2016-0773/patches.c: -------------------------------------------------------------------------------- 1 | #include "regex/regguts.h" 2 | 3 | #include "upatch_private.h" 4 | 5 | /* * definitions from src/backend/regex/regcomp.c * * * * * * * * */ 6 | 7 | /* internal variables, bundled for easy passing around */ 8 | struct vars 9 | { 10 | regex_t *re; 11 | const chr *now; /* scan pointer into string */ 12 | const chr *stop; /* end of string */ 13 | const chr *savenow; /* saved now and stop for "subroutine call" */ 14 | const chr *savestop; 15 | int err; /* error code (0 if none) */ 16 | int cflags; /* copy of compile flags */ 17 | int lasttype; /* type of previous token */ 18 | int nexttype; /* type of next token */ 19 | chr nextvalue; /* value (if any) of next token */ 20 | int lexcon; /* lexical context type (see lex.c) */ 21 | int nsubexp; /* subexpression count */ 22 | struct subre **subs; /* subRE pointer vector */ 23 | size_t nsubs; /* length of vector */ 24 | struct subre *sub10[10]; /* initial vector, enough for most */ 25 | struct nfa *nfa; /* the NFA */ 26 | struct colormap *cm; /* character color map */ 27 | color nlcolor; /* color of newline */ 28 | struct state *wordchrs; /* state in nfa holding word-char outarcs */ 29 | struct subre *tree; /* subexpression tree */ 30 | struct subre *treechain; /* all tree nodes allocated */ 31 | struct subre *treefree; /* any free tree nodes */ 32 | int ntree; /* number of tree nodes, plus one */ 33 | struct cvec *cv; /* interface cvec */ 34 | struct cvec *cv2; /* utility cvec */ 35 | struct subre *lacons; /* lookahead-constraint vector */ 36 | int nlacons; /* size of lacons */ 37 | }; 38 | 39 | /* parsing macros; most know that `v' is the struct vars pointer */ 40 | #define NEXT() (next(v)) /* advance by one token */ 41 | #define SEE(t) (v->nexttype == (t)) /* is next token this? */ 42 | #define EAT(t) (SEE(t) && next(v)) /* if next is this, swallow it */ 43 | #define VISERR(vv) ((vv)->err != 0) /* have we seen an error yet? */ 44 | #define ISERR() VISERR(v) 45 | #define VERR(vv,e) ((vv)->nexttype = EOS, \ 46 | (vv)->err = ((vv)->err ? (vv)->err : (e))) 47 | #define ERR(e) VERR(v, e) /* record an error */ 48 | #define NOERR() {if (ISERR()) return;} /* if error seen, return */ 49 | #define NOERRN() {if (ISERR()) return NULL;} /* NOERR with retval */ 50 | #define NOERRZ() {if (ISERR()) return 0;} /* NOERR with retval */ 51 | #define INSIST(c, e) do { if (!(c)) ERR(e); } while (0) /* error if c false */ 52 | #define NOTE(b) (v->re->re_info |= (b)) /* note visible condition */ 53 | #define EMPTYARC(x, y) newarc(v->nfa, EMPTY, 0, x, y) 54 | 55 | /* token type codes, some also used as NFA arc types */ 56 | #define EMPTY 'n' /* no token present */ 57 | #define EOS 'e' /* end of string */ 58 | #define PLAIN 'p' /* ordinary character */ 59 | #define DIGIT 'd' /* digit (in bound) */ 60 | #define BACKREF 'b' /* back reference */ 61 | #define COLLEL 'I' /* start of [. */ 62 | #define ECLASS 'E' /* start of [= */ 63 | #define CCLASS 'C' /* start of [: */ 64 | #define END 'X' /* end of [. [= [: */ 65 | #define RANGE 'R' /* - within [] which might be range delim. */ 66 | #define LACON 'L' /* lookahead constraint subRE */ 67 | #define AHEAD 'a' /* color-lookahead arc */ 68 | #define BEHIND 'r' /* color-lookbehind arc */ 69 | #define WBDRY 'w' /* word boundary constraint */ 70 | #define NWBDRY 'W' /* non-word-boundary constraint */ 71 | #define SBEGIN 'A' /* beginning of string (even if not BOL) */ 72 | #define SEND 'Z' /* end of string (even if not EOL) */ 73 | #define PREFER 'P' /* length preference */ 74 | 75 | 76 | /* * definitions from src/backend/regex/regc_lex.c * * * * * * * * */ 77 | 78 | /* scanning macros (know about v) */ 79 | #define ATEOS() (v->now >= v->stop) 80 | #define HAVE(n) (v->stop - v->now >= (n)) 81 | #define NEXT1(c) (!ATEOS() && *v->now == CHR(c)) 82 | #define NEXT2(a,b) (HAVE(2) && *v->now == CHR(a) && *(v->now+1) == CHR(b)) 83 | #define NEXT3(a,b,c) (HAVE(3) && *v->now == CHR(a) && \ 84 | *(v->now+1) == CHR(b) && \ 85 | *(v->now+2) == CHR(c)) 86 | #define SET(c) (v->nexttype = (c)) 87 | #define SETV(c, n) (v->nexttype = (c), v->nextvalue = (n)) 88 | #define RET(c) return (SET(c), 1) 89 | #define RETV(c, n) return (SETV(c, n), 1) 90 | #define FAILW(e) return (ERR(e), 0) /* ERR does SET(EOS) */ 91 | #define LASTTYPE(t) (v->lasttype == (t)) 92 | 93 | /* construct pointer past end of chr array */ 94 | #define ENDOF(array) ((array) + sizeof(array)/sizeof(chr)) 95 | 96 | 97 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 98 | /* * patched version of lexescape * * * * * * * * * * * * * * * * */ 99 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 100 | 101 | /* lexescape() is the only user of CHR_MAX, so just re-defined it to 102 | * the patched version here. */ 103 | #undef CHR_MAX 104 | #define CHR_MAX 0x7ffffffe 105 | 106 | int patched_lexescape(struct vars *); 107 | 108 | /* 109 | * lexescape - parse an ARE backslash escape (backslash already eaten) 110 | * Note slightly nonstandard use of the CCLASS type code. 111 | */ 112 | int /* not actually used, but convenient for RETV */ 113 | patched_lexescape(struct vars * v) 114 | { 115 | chr c; 116 | static chr alert[] = { 117 | CHR('a'), CHR('l'), CHR('e'), CHR('r'), CHR('t') 118 | }; 119 | static chr esc[] = { 120 | CHR('E'), CHR('S'), CHR('C') 121 | }; 122 | const chr *save; 123 | 124 | assert(v->cflags & REG_ADVF); 125 | 126 | assert(!ATEOS()); 127 | c = *v->now++; 128 | if (!iscalnum(c)) 129 | RETV(PLAIN, c); 130 | 131 | NOTE(REG_UNONPOSIX); 132 | switch (c) 133 | { 134 | case CHR('a'): 135 | RETV(PLAIN, chrnamed(v, alert, ENDOF(alert), CHR('\007'))); 136 | break; 137 | case CHR('A'): 138 | RETV(SBEGIN, 0); 139 | break; 140 | case CHR('b'): 141 | RETV(PLAIN, CHR('\b')); 142 | break; 143 | case CHR('B'): 144 | RETV(PLAIN, CHR('\\')); 145 | break; 146 | case CHR('c'): 147 | NOTE(REG_UUNPORT); 148 | if (ATEOS()) 149 | FAILW(REG_EESCAPE); 150 | RETV(PLAIN, (chr) (*v->now++ & 037)); 151 | break; 152 | case CHR('d'): 153 | NOTE(REG_ULOCALE); 154 | RETV(CCLASS, 'd'); 155 | break; 156 | case CHR('D'): 157 | NOTE(REG_ULOCALE); 158 | RETV(CCLASS, 'D'); 159 | break; 160 | case CHR('e'): 161 | NOTE(REG_UUNPORT); 162 | RETV(PLAIN, chrnamed(v, esc, ENDOF(esc), CHR('\033'))); 163 | break; 164 | case CHR('f'): 165 | RETV(PLAIN, CHR('\f')); 166 | break; 167 | case CHR('m'): 168 | RET('<'); 169 | break; 170 | case CHR('M'): 171 | RET('>'); 172 | break; 173 | case CHR('n'): 174 | RETV(PLAIN, CHR('\n')); 175 | break; 176 | case CHR('r'): 177 | RETV(PLAIN, CHR('\r')); 178 | break; 179 | case CHR('s'): 180 | NOTE(REG_ULOCALE); 181 | RETV(CCLASS, 's'); 182 | break; 183 | case CHR('S'): 184 | NOTE(REG_ULOCALE); 185 | RETV(CCLASS, 'S'); 186 | break; 187 | case CHR('t'): 188 | RETV(PLAIN, CHR('\t')); 189 | break; 190 | case CHR('u'): 191 | c = lexdigits(v, 16, 4, 4); 192 | if (ISERR() || c < CHR_MIN || c > CHR_MAX) 193 | FAILW(REG_EESCAPE); 194 | RETV(PLAIN, c); 195 | break; 196 | case CHR('U'): 197 | c = lexdigits(v, 16, 8, 8); 198 | if (ISERR() || c < CHR_MIN || c > CHR_MAX) 199 | FAILW(REG_EESCAPE); 200 | RETV(PLAIN, c); 201 | break; 202 | case CHR('v'): 203 | RETV(PLAIN, CHR('\v')); 204 | break; 205 | case CHR('w'): 206 | NOTE(REG_ULOCALE); 207 | RETV(CCLASS, 'w'); 208 | break; 209 | case CHR('W'): 210 | NOTE(REG_ULOCALE); 211 | RETV(CCLASS, 'W'); 212 | break; 213 | case CHR('x'): 214 | NOTE(REG_UUNPORT); 215 | c = lexdigits(v, 16, 1, 255); /* REs >255 long outside spec */ 216 | if (ISERR() || c < CHR_MIN || c > CHR_MAX) 217 | FAILW(REG_EESCAPE); 218 | RETV(PLAIN, c); 219 | break; 220 | case CHR('y'): 221 | NOTE(REG_ULOCALE); 222 | RETV(WBDRY, 0); 223 | break; 224 | case CHR('Y'): 225 | NOTE(REG_ULOCALE); 226 | RETV(NWBDRY, 0); 227 | break; 228 | case CHR('Z'): 229 | RETV(SEND, 0); 230 | break; 231 | case CHR('1'): 232 | case CHR('2'): 233 | case CHR('3'): 234 | case CHR('4'): 235 | case CHR('5'): 236 | case CHR('6'): 237 | case CHR('7'): 238 | case CHR('8'): 239 | case CHR('9'): 240 | save = v->now; 241 | v->now--; /* put first digit back */ 242 | c = lexdigits(v, 10, 1, 255); /* REs >255 long outside spec */ 243 | if (ISERR()) 244 | FAILW(REG_EESCAPE); 245 | /* ugly heuristic (first test is "exactly 1 digit?") */ 246 | if (v->now == save || ((int) c > 0 && (int) c <= v->nsubexp)) 247 | { 248 | NOTE(REG_UBACKREF); 249 | RETV(BACKREF, (chr) c); 250 | } 251 | /* oops, doesn't look like it's a backref after all... */ 252 | v->now = save; 253 | /* and fall through into octal number */ 254 | case CHR('0'): 255 | NOTE(REG_UUNPORT); 256 | v->now--; /* put first digit back */ 257 | c = lexdigits(v, 8, 1, 3); 258 | if (ISERR()) 259 | FAILW(REG_EESCAPE); 260 | RETV(PLAIN, c); 261 | break; 262 | default: 263 | assert(iscalpha(c)); 264 | FAILW(REG_EESCAPE); /* unknown alphabetic escape */ 265 | break; 266 | } 267 | assert(NOTREACHED); 268 | } 269 | 270 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 271 | /* Can't find these symbols in /usr/lib/debug/usr/bin/postgres.debug, 272 | * so I'm asusming they were inlined. Provide our own copies here. */ 273 | 274 | /* 275 | * before - is chr x before chr y, for purposes of range legality? 276 | */ 277 | static int /* predicate */ 278 | before(chr x, chr y) 279 | { 280 | if (x < y) 281 | return 1; 282 | return 0; 283 | } 284 | 285 | /* 286 | * addchr - add a chr to a cvec 287 | */ 288 | static void 289 | addchr(struct cvec * cv, /* character vector */ 290 | chr c) /* character to add */ 291 | { 292 | assert(cv->nchrs < cv->chrspace); 293 | cv->chrs[cv->nchrs++] = (chr) c; 294 | } 295 | 296 | /* 297 | * addrange - add a range to a cvec 298 | */ 299 | static void 300 | addrange(struct cvec * cv, /* character vector */ 301 | chr from, /* first character of range */ 302 | chr to) /* last character of range */ 303 | { 304 | assert(cv->nranges < cv->rangespace); 305 | cv->ranges[cv->nranges * 2] = (chr) from; 306 | cv->ranges[cv->nranges * 2 + 1] = (chr) to; 307 | cv->nranges++; 308 | } 309 | 310 | 311 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 312 | /* * patched version of range * * * * * * * * * * * * * * * * * * */ 313 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 314 | 315 | struct cvec *patched_range(struct vars * v, celt a, celt b, int cases); 316 | 317 | /* 318 | * range - supply cvec for a range, including legality check 319 | */ 320 | struct cvec * 321 | patched_range(struct vars * v, /* context */ 322 | celt a, /* range start */ 323 | celt b, /* range end, might equal a */ 324 | int cases) /* case-independent? */ 325 | { 326 | int nchrs; 327 | struct cvec *cv; 328 | celt c, 329 | cc; 330 | 331 | if (a != b && !before(a, b)) 332 | { 333 | ERR(REG_ERANGE); 334 | return NULL; 335 | } 336 | 337 | if (!cases) 338 | { /* easy version */ 339 | cv = getcvec(v, 0, 1); 340 | NOERRN(); 341 | addrange(cv, a, b); 342 | return cv; 343 | } 344 | 345 | /* 346 | * When case-independent, it's hard to decide when cvec ranges are usable, 347 | * so for now at least, we won't try. We use a range for the originally 348 | * specified chrs and then add on any case-equivalents that are outside 349 | * that range as individual chrs. 350 | * 351 | * To ensure sane behavior if someone specifies a very large range, limit 352 | * the allocation size to 100000 chrs (arbitrary) and check for overrun 353 | * inside the loop below. 354 | */ 355 | 356 | nchrs = b - a + 1; 357 | if (nchrs <= 0 || nchrs > 100000) 358 | nchrs = 100000; 359 | 360 | cv = getcvec(v, nchrs, 1); 361 | NOERRN(); 362 | addrange(cv, a, b); 363 | 364 | for (c = a; c <= b; c++) 365 | { 366 | cc = pg_wc_tolower((chr) c); 367 | if (cc != c && 368 | (before(cc, a) || before(b, cc))) 369 | { 370 | if (cv->nchrs >= cv->chrspace) 371 | { 372 | ERR(REG_ETOOBIG); 373 | return NULL; 374 | } 375 | addchr(cv, cc); 376 | } 377 | cc = pg_wc_toupper((chr) c); 378 | if (cc != c && 379 | (before(cc, a) || before(b, cc))) 380 | { 381 | if (cv->nchrs >= cv->chrspace) 382 | { 383 | ERR(REG_ETOOBIG); 384 | return NULL; 385 | } 386 | addchr(cv, cc); 387 | } 388 | if (CANCEL_REQUESTED(v->re)) 389 | { 390 | ERR(REG_CANCEL); 391 | return NULL; 392 | } 393 | } 394 | 395 | return cv; 396 | } 397 | 398 | 399 | /* * definitions from src/backend/regex/regc_color.c * * * * * * * */ 400 | 401 | #define CERR(e) VERR(cm->v, (e)) 402 | 403 | /* 404 | * subblock - allocate new subcolors for one tree block of chrs, fill in arcs 405 | * 406 | * Note: subcolors that are created during execution of this function 407 | * will not be given a useful value of firstchr; it'll be left as CHR_MIN. 408 | * For the current usage of firstchr in pg_regprefix, this does not matter 409 | * because such subcolors won't occur in the common prefix of a regex. 410 | */ 411 | static void 412 | subblock(struct vars * v, 413 | chr start, /* first of BYTTAB chrs */ 414 | struct state * lp, 415 | struct state * rp) 416 | { 417 | uchr uc = start; 418 | struct colormap *cm = v->cm; 419 | int shift; 420 | int level; 421 | int i; 422 | int b; 423 | union tree *t; 424 | union tree *cb; 425 | union tree *fillt; 426 | union tree *lastt; 427 | int previ; 428 | int ndone; 429 | color co; 430 | color sco; 431 | 432 | assert((uc % BYTTAB) == 0); 433 | 434 | /* find its color block, making new pointer blocks as needed */ 435 | t = cm->tree; 436 | fillt = NULL; 437 | for (level = 0, shift = BYTBITS * (NBYTS - 1); shift > 0; 438 | level++, shift -= BYTBITS) 439 | { 440 | b = (uc >> shift) & BYTMASK; 441 | lastt = t; 442 | t = lastt->tptr[b]; 443 | assert(t != NULL); 444 | fillt = &cm->tree[level + 1]; 445 | if (t == fillt && shift > BYTBITS) 446 | { /* need new ptr block */ 447 | t = (union tree *) MALLOC(sizeof(struct ptrs)); 448 | if (t == NULL) 449 | { 450 | CERR(REG_ESPACE); 451 | return; 452 | } 453 | memcpy(VS(t->tptr), VS(fillt->tptr), 454 | BYTTAB * sizeof(union tree *)); 455 | lastt->tptr[b] = t; 456 | } 457 | } 458 | 459 | /* special cases: fill block or solid block */ 460 | co = t->tcolor[0]; 461 | cb = cm->cd[co].block; 462 | if (t == fillt || t == cb) 463 | { 464 | /* either way, we want a subcolor solid block */ 465 | sco = newsub(cm, co); 466 | t = cm->cd[sco].block; 467 | if (t == NULL) 468 | { /* must set it up */ 469 | t = (union tree *) MALLOC(sizeof(struct colors)); 470 | if (t == NULL) 471 | { 472 | CERR(REG_ESPACE); 473 | return; 474 | } 475 | for (i = 0; i < BYTTAB; i++) 476 | t->tcolor[i] = sco; 477 | cm->cd[sco].block = t; 478 | } 479 | /* find loop must have run at least once */ 480 | lastt->tptr[b] = t; 481 | newarc(v->nfa, PLAIN, sco, lp, rp); 482 | cm->cd[co].nchrs -= BYTTAB; 483 | cm->cd[sco].nchrs += BYTTAB; 484 | return; 485 | } 486 | 487 | /* general case, a mixed block to be altered */ 488 | i = 0; 489 | while (i < BYTTAB) 490 | { 491 | co = t->tcolor[i]; 492 | sco = newsub(cm, co); 493 | newarc(v->nfa, PLAIN, sco, lp, rp); 494 | previ = i; 495 | do 496 | { 497 | t->tcolor[i++] = sco; 498 | } while (i < BYTTAB && t->tcolor[i] == co); 499 | ndone = i - previ; 500 | cm->cd[co].nchrs -= ndone; 501 | cm->cd[sco].nchrs += ndone; 502 | } 503 | } 504 | 505 | /* 506 | * subrange - allocate new subcolors to this range of chrs, fill in arcs 507 | */ 508 | static void 509 | subrange(struct vars * v, 510 | chr from, 511 | chr to, 512 | struct state * lp, 513 | struct state * rp) 514 | { 515 | uchr uf; 516 | int i; 517 | 518 | assert(from <= to); 519 | 520 | /* first, align "from" on a tree-block boundary */ 521 | uf = (uchr) from; 522 | i = (int) (((uf + BYTTAB - 1) & (uchr) ~BYTMASK) - uf); 523 | for (; from <= to && i > 0; i--, from++) 524 | newarc(v->nfa, PLAIN, subcolor(v->cm, from), lp, rp); 525 | if (from > to) /* didn't reach a boundary */ 526 | return; 527 | 528 | /* deal with whole blocks */ 529 | for (; to - from >= BYTTAB; from += BYTTAB) 530 | subblock(v, from, lp, rp); 531 | 532 | /* clean up any remaining partial table */ 533 | for (; from <= to; from++) 534 | newarc(v->nfa, PLAIN, subcolor(v->cm, from), lp, rp); 535 | } 536 | 537 | 538 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 539 | /* * patched version of dovec * * * * * * * * * * * * * * * * * * */ 540 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 541 | 542 | void patched_dovec(struct vars *, struct cvec *, struct state *, struct state *); 543 | 544 | /* 545 | * dovec - fill in arcs for each element of a cvec 546 | */ 547 | void 548 | patched_dovec(struct vars * v, 549 | struct cvec * cv, 550 | struct state * lp, 551 | struct state * rp) 552 | { 553 | chr ch, 554 | from, 555 | to; 556 | const chr *p; 557 | int i; 558 | 559 | /* ordinary characters */ 560 | for (p = cv->chrs, i = cv->nchrs; i > 0; p++, i--) 561 | { 562 | ch = *p; 563 | newarc(v->nfa, PLAIN, subcolor(v->cm, ch), lp, rp); 564 | NOERR(); 565 | } 566 | 567 | /* and the ranges */ 568 | for (p = cv->ranges, i = cv->nranges; i > 0; p += 2, i--) 569 | { 570 | from = *p; 571 | to = *(p + 1); 572 | if (from <= to) 573 | subrange(v, from, to, lp, rp); 574 | NOERR(); 575 | } 576 | } 577 | 578 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 579 | --------------------------------------------------------------------------------