├── .gitignore ├── main.c ├── utils ├── Makefile ├── utils.c ├── asm.h ├── inject.h ├── ptrace.h ├── utils.h ├── inject.c └── ptrace.c ├── sample-library.c ├── arch └── x86_64 │ ├── Makefile │ ├── inject-content.S │ └── inject.c ├── thread.cpp ├── sample-target.cpp ├── signal-checker.c ├── README.md ├── sample-ptrace.c ├── Makefile ├── Makefile.include └── epoll_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | tags 3 | cscope* 4 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern int inject(char *libname, pid_t pid); 5 | 6 | int main(int argc, char** argv) 7 | { 8 | char *libname = argv[1]; 9 | pid_t pid = atoi(argv[2]); 10 | 11 | inject(libname, pid); 12 | } 13 | -------------------------------------------------------------------------------- /utils/Makefile: -------------------------------------------------------------------------------- 1 | include $(srcdir)/Makefile.include 2 | 3 | sdir := $(srcdir)/utils 4 | odir := $(objdir)/utils 5 | 6 | UTILS_SRCS := $(wildcard *.c) 7 | 8 | all: $(odir)/%.o 9 | 10 | $(odir)/%.o: $(UTILS_SRCS) 11 | $(CC) $(COMMON_CFLAGS) -c $(UTILS_SRCS) 12 | 13 | clean: 14 | $(RM) $(odir)/*.o 15 | -------------------------------------------------------------------------------- /utils/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "utils.h" 3 | 4 | void __pr_dbg(const char *fmt, ...) 5 | { 6 | va_list ap; 7 | 8 | va_start(ap, fmt); 9 | vfprintf(stderr, fmt, ap); 10 | va_end(ap); 11 | } 12 | 13 | void __pr_err(const char *fmt, ...) 14 | { 15 | va_list ap; 16 | 17 | va_start(ap, fmt); 18 | vfprintf(stderr, fmt, ap); 19 | va_end(ap); 20 | 21 | exit(1); 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /utils/asm.h: -------------------------------------------------------------------------------- 1 | #define GLOBAL(sym) \ 2 | .global sym; \ 3 | .type sym, %function; \ 4 | sym: \ 5 | .global uftrace_ ## sym; \ 6 | .hidden uftrace_ ## sym; \ 7 | .type uftrace_ ## sym, %function; \ 8 | uftrace_ ## sym: 9 | 10 | #define ENTRY(sym) \ 11 | .global sym; \ 12 | .hidden sym; \ 13 | .type sym, %function; \ 14 | sym: 15 | 16 | #define END(sym) \ 17 | .size sym, .-sym; 18 | 19 | -------------------------------------------------------------------------------- /utils/inject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux module for injecting shared object to the process by using ptrace. 3 | * 4 | * copied from: https://github.com/gaffe23/linux-inject 5 | * Released under the GPL v2+. 6 | */ 7 | long get_inject_code_addr(pid_t pid); 8 | long get_so_addr(pid_t pid, char *so_name); 9 | 10 | long get_libc_addr(pid_t pid); 11 | long get_libdl_addr(pid_t pid); 12 | 13 | int check_loaded(pid_t pid, char *lib_name); 14 | long get_function_addr(char *so_name, char *func_name); 15 | unsigned char *find_ret(void *end_addr); 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /arch/x86_64/Makefile: -------------------------------------------------------------------------------- 1 | include $(srcdir)/Makefile.include 2 | 3 | sdir := $(srcdir)/arch/x86_64 4 | odir := $(objdir)/arch/x86_64 5 | 6 | ifeq ($(USE_LIBC_DLOPEN), 1) 7 | COMMON_CFLAGS += -DUSE_LIBC_DLOPEN 8 | else 9 | COMMON_CFLAGS += -DUSE_LIBDL_DLOPEN 10 | endif 11 | 12 | all: $(odir)/arch.o 13 | 14 | $(odir)/arch.o: $(odir)/inject.op $(odir)/inject-contents.op 15 | $(LD) -r -o $@ $(sdir)/inject.op $(sdir)/inject-contents.op 16 | 17 | $(odir)/inject-contents.op: 18 | $(CC) $(LIB_CFLAGS) -c $(sdir)/inject-content.S -o $@ 19 | 20 | $(odir)/inject.op: 21 | $(CC) $(LIB_CFLAGS) -c $(sdir)/inject.c -o $@ 22 | 23 | clean: 24 | $(RM) $(odir)/*.op 25 | $(RM) $(odir)/*.o 26 | -------------------------------------------------------------------------------- /utils/ptrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux module for injecting shared object to the process by using ptrace. 3 | * 4 | * copied from: https://github.com/gaffe23/linux-inject 5 | * Released under the GPL v2+. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef ARM 13 | #define REG_TYPE user_regs 14 | #else 15 | #define REG_TYPE user_regs_struct 16 | #endif 17 | 18 | void ptrace_attach(pid_t target); 19 | void ptrace_detach(pid_t target); 20 | void ptrace_getregs(pid_t target, struct REG_TYPE *regs); 21 | void ptrace_cont(pid_t target); 22 | void ptrace_setregs(pid_t target, struct REG_TYPE *regs); 23 | void ptrace_getsiginfo(pid_t target, siginfo_t *targetsig); 24 | void ptrace_read(int pid, unsigned long addr, void *vptr, int len); 25 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len); 26 | void check_target_sig(int pid); 27 | void restore_state_and_detach(pid_t target, unsigned long addr, void *backup, int datasize, struct REG_TYPE oldregs); 28 | -------------------------------------------------------------------------------- /thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | 11 | static int test = 0; 12 | static int test1 = 0; 13 | 14 | atomic n(0); 15 | 16 | void *thread_fn(void *arg) 17 | { 18 | int self_number = *(int *)arg; 19 | 20 | printf("pid: %d\n", syscall(SYS_gettid)); 21 | printf("addr: %p\n", &test1); 22 | 23 | while (1) { 24 | test1++; 25 | } 26 | return NULL; 27 | } 28 | 29 | void do_fork(int arg) 30 | { 31 | int self_number = arg; 32 | test = arg; 33 | int pid = fork(); 34 | if (pid == 0) { 35 | while (1) { 36 | test++; 37 | n.fetch_add(1, std::memory_order_relaxed); 38 | } 39 | } 40 | } 41 | 42 | int main(int argc, char *argv[]) 43 | { 44 | pthread_t thid1; 45 | pthread_t thid2; 46 | pthread_t thid3; 47 | 48 | printf("pid: %d\n", getpid()); 49 | printf("addr: %p\n", &test); 50 | 51 | int tid1 = 210001; 52 | pthread_create(&thid1, NULL, thread_fn, &tid1); 53 | 54 | while (1) { 55 | test++; 56 | } 57 | pthread_join(thid1, NULL); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /sample-target.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | 11 | static int test = 0; 12 | static int test1 = 0; 13 | 14 | atomic n(0); 15 | 16 | void *thread_fn(void *arg) 17 | { 18 | int self_number = *(int *)arg; 19 | 20 | printf("pid: %d\n", syscall(SYS_gettid)); 21 | printf("addr: %p\n", &test1); 22 | 23 | while (1) { 24 | test1++; 25 | } 26 | return NULL; 27 | } 28 | 29 | void do_fork(int arg) 30 | { 31 | int self_number = arg; 32 | test = arg; 33 | int pid = fork(); 34 | if (pid == 0) { 35 | while (1) { 36 | test++; 37 | n.fetch_add(1, std::memory_order_relaxed); 38 | } 39 | } 40 | } 41 | 42 | int main(int argc, char *argv[]) 43 | { 44 | pthread_t thid1; 45 | pthread_t thid2; 46 | pthread_t thid3; 47 | 48 | printf("pid: %d\n", getpid()); 49 | printf("addr: %p\n", &test); 50 | 51 | int tid1 = 210001; 52 | pthread_create(&thid1, NULL, thread_fn, &tid1); 53 | 54 | while (1) { 55 | test++; 56 | } 57 | pthread_join(thid1, NULL); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /signal-checker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct sigaction act; 9 | 10 | void sighandler(int signum, siginfo_t *info, void *ptr) 11 | { 12 | fprintf(stderr, "Received signal %d\n", signum); 13 | fprintf(stderr, "Signal originates from process %lu\n", 14 | (unsigned long)info->si_pid); 15 | } 16 | 17 | void setup_sighandler() 18 | { 19 | printf("I am %lu\n", (unsigned long)getpid()); 20 | 21 | memset(&act, 0, sizeof(act)); 22 | 23 | act.sa_sigaction = sighandler; 24 | act.sa_flags = SA_SIGINFO; 25 | 26 | for(int i = 0; i < 32; i++) { 27 | sigaction(i, &act, NULL); 28 | } 29 | } 30 | 31 | void hello() 32 | { 33 | fprintf(stderr, "Hello, I'm loaded!!\n"); 34 | } 35 | 36 | 37 | /* 38 | * loadMsg() 39 | * 40 | * This function is automatically called when the sample library is injected 41 | * into a process. It calls hello() to output a message indicating that the 42 | * library has been loaded. 43 | * 44 | */ 45 | __attribute__((constructor)) 46 | void loadMsg() 47 | { 48 | setup_sighandler(); 49 | hello(); 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux so injector 2 | 3 | Requirement 4 | 1. only support x86 64bit architecture. 5 | 6 | 7 | This project aims to inject a Shared object into a process running on Linux. 8 | - Supports multi-thread safe. 9 | - Sacrificed stability for fastest injection. 10 | 11 | Test in Ubuntu 18.04 64bit. 12 | 13 | # Example 14 | 15 | ``` 16 | $ make 17 | $ ./sample-target 18 | pid: 72935 19 | addr: 0x601058 20 | pid: 72936 21 | addr: 0x60105c 22 | ``` 23 | 24 | ``` 25 | # open another terminal to run the injector 26 | $ ./injector sample-library.so 72935 27 | injector: size of code to inject 207 28 | injector: Path of Shared object to be injected : /home/m/git/linux_so_injector/sample-library.so 29 | injector: RIP Register : 400f32 30 | ``` 31 | 32 | then you can see the messsage that shared object has printed. 33 | ``` 34 | $ ./sample-target 35 | pid: 72935 36 | addr: 0x601058 37 | pid: 72936 38 | addr: 0x60105c 39 | I just got loaded 40 | ``` 41 | 42 | # reference 43 | https://github.com/gaffe23/linux-inject 44 | 45 | 46 | # etc 47 | run this command when you see the "ptrace attach failed" message. 48 | ``` 49 | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 50 | ``` 51 | 52 | ## Lincese 53 | GPLv2 54 | -------------------------------------------------------------------------------- /sample-ptrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "utils/inject.h" 6 | #include "utils/ptrace.h" 7 | #include "utils/utils.h" 8 | 9 | int main(int argc, char** argv) 10 | { 11 | struct user_regs_struct oldregs, regs; 12 | siginfo_t sig; 13 | long read = 0L; 14 | 15 | pid_t pid = atoi(argv[1]); 16 | 17 | printf("Trying to attach process %d\n", pid); 18 | ptrace_attach(pid); 19 | ptrace_getregs(pid, &oldregs); 20 | ptrace_setregs(pid, &oldregs); 21 | 22 | read = ptrace(PTRACE_PEEKTEXT, pid, oldregs.rip, 0L); 23 | printf("Read from process %lx\n", read); 24 | 25 | printf("Trying to write process %lx\n", read); 26 | ptrace(PTRACE_POKETEXT, pid, oldregs.rip, read); 27 | 28 | ptrace(PTRACE_GETSIGINFO, pid, 0L, &sig); 29 | printf("SIGINFO sigcode %d, signum : %s %d\n", 30 | sig.si_code, 31 | strsignal(sig.si_signo), sig.si_signo); 32 | 33 | 34 | printf("Trying to cont process %d\n", pid); 35 | ptrace(PTRACE_CONT, pid, 0L, 0L); 36 | 37 | ptrace(PTRACE_GETSIGINFO, pid, 0L, &sig); 38 | printf("SIGINFO sigcode %d, signum : %s %d\n", 39 | sig.si_code, 40 | strsignal(sig.si_signo), sig.si_signo); 41 | 42 | 43 | 44 | printf("Trying to detach process %d\n", pid); 45 | ptrace(PTRACE_DETACH, pid, 0L, 0L); 46 | 47 | getchar(); 48 | } 49 | -------------------------------------------------------------------------------- /utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H_INCLUDED 2 | #define TEST_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #ifndef PR_FMT 11 | # define PR_FMT "injector" 12 | #endif 13 | 14 | extern FILE *stdin; 15 | extern FILE *stdout; 16 | extern FILE *stderr; 17 | 18 | extern void __pr_dbg(const char *fmt, ...); 19 | extern void __pr_err(const char *fmt, ...); 20 | 21 | #define pr_dbg(fmt, ...) \ 22 | ({ \ 23 | __pr_dbg(PR_FMT ": " fmt, ## __VA_ARGS__); \ 24 | }) 25 | 26 | #define pr_err(fmt, ...) \ 27 | __pr_err(PR_FMT ": %s:%d:%s\n ERROR: " fmt, \ 28 | __FILE__, __LINE__, __func__, ## __VA_ARGS__) 29 | 30 | #endif 31 | 32 | #define xcalloc(sz, n) \ 33 | ({ void *__ptr = calloc(sz, n); \ 34 | if (__ptr == NULL) { \ 35 | pr_err("xcalloc"); \ 36 | } \ 37 | __ptr; \ 38 | }) 39 | 40 | 41 | #define xmalloc(sz) \ 42 | ({ void *__ptr = malloc(sz); \ 43 | if (__ptr == NULL) { \ 44 | pr_err("xmalloc"); \ 45 | } \ 46 | __ptr; \ 47 | }) 48 | 49 | #ifndef ALIGN 50 | # define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1)) 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION := 0.0.1 2 | 3 | srcdir = $(CURDIR) 4 | ifeq ($(objdir),) 5 | ifneq ($(O),) 6 | objdir = $(O) 7 | else 8 | objdir = $(CURDIR) 9 | endif 10 | endif 11 | 12 | include $(srcdir)/Makefile.include 13 | export srcdir objdir 14 | 15 | ARCH_OBJ := $(srcdir)/arch/$(ARCH)/arch.o 16 | UTILS_SRCS := $(wildcard $(srcdir)/utils/*.c) 17 | UTILS_OBJS := $(patsubst $(srcdir)/utils/%.c,$(objdir)/utils/%.o,$(UTILS_SRCS)) 18 | 19 | build-utils: 20 | @$(MAKE) -C $(srcdir)/utils 21 | 22 | build-arch: 23 | @$(MAKE) -C $(srcdir)/arch/x86_64 24 | 25 | build-main: $(objdir)/main.o 26 | $(objdir)/main.o: $(srcdir)/main.c 27 | $(CC) -o $@ $(COMMON_CFLAGS) -c $^ 28 | 29 | build: $(objdir)/injector 30 | $(objdir)/injector: $(objdir)/main.o 31 | $(CC) $(ARCH_OBJ) $(UTILS_OBJS) $^ $(COMMON_CFLAGS) -o $@ $(COMMON_LDFLAGS) 32 | 33 | all: 34 | @$(MAKE) -s build-utils 35 | @$(MAKE) -s build-arch 36 | @$(MAKE) -s build-main 37 | @$(MAKE) -s build 38 | @$(MAKE) -s sample-library 39 | @$(MAKE) -s sample-target 40 | @$(MAKE) -s sample-ptrace 41 | @$(MAKE) -s signal-checker 42 | 43 | sample-ptrace: sample-ptrace.c 44 | $(CC) -o $@ $^ $(UTILS_OBJS) $(LDFLAGS) -ldl 45 | 46 | sample-library: sample-library.c 47 | $(CC) -o $@.so $^ $(CFLAGS) $(LDFLAGS) -shared -fPIC 48 | 49 | signal-checker: signal-checker.c 50 | $(CC) -o $@.so $^ $(CFLAGS) $(LDFLAGS) -shared -fPIC 51 | 52 | sample-target: sample-target.cpp 53 | $(CC) -o $@ $^ $(TARGET_CFLAGS) $(LDFLAGS) -lpthread -O0 54 | 55 | clean: 56 | @$(MAKE) -C arch/x86_64 clean 57 | @$(MAKE) -C utils clean 58 | $(Q)$(RM) $(objdir)/injector 59 | $(Q)$(RM) $(objdir)/sample-ptrace $(objdir)/signal-checker 60 | $(Q)$(RM) $(objdir)/sample-library 61 | $(Q)$(RM) $(objdir)/sample-target 62 | $(Q)$(RM) $(objdir)/*.o $(objdir)/*.so 63 | 64 | .PHONY: all clean PHONY 65 | .DEFAULT_GOAL := all 66 | -------------------------------------------------------------------------------- /utils/inject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define PR_FMT "dynamic" 9 | #define PR_DOMAIN DBG_DYNAMIC 10 | #include "utils/inject.h" 11 | #include "utils/utils.h" 12 | 13 | 14 | // define max line that read from /proc//map 15 | #define PATH_MAX 4096 16 | 17 | long get_inject_code_addr(pid_t pid) 18 | { 19 | FILE *fp; 20 | char filename[30]; 21 | char line[PATH_MAX]; 22 | long start, end, result = 0; 23 | char str[20]; 24 | char perms[5]; 25 | char *fmt = "/proc/%d/maps"; 26 | 27 | snprintf(filename, sizeof(filename), fmt, pid); 28 | fp = fopen(filename, "r"); 29 | if (fp == NULL) 30 | pr_err("cannot open /proc/%d/maps", pid); 31 | while (fgets(line, sizeof(line), fp) != NULL) { 32 | sscanf(line, "%lx-%lx %s %*s %s %*d", &start, &end, perms, str); 33 | if (strstr(perms, "x") != NULL) { 34 | result = end; 35 | break; 36 | } 37 | } 38 | fclose(fp); 39 | return result; 40 | } 41 | 42 | long get_so_addr(pid_t pid, char *so_name) 43 | { 44 | FILE *fp; 45 | char filename[30]; 46 | char line[PATH_MAX]; 47 | long addr, result=0; 48 | 49 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 50 | fp = fopen(filename, "r"); 51 | if (fp == NULL) 52 | pr_err("cannot open /proc/%d/maps", pid); 53 | while (fgets(line, PATH_MAX, fp) != NULL) { 54 | sscanf(line, "%lx-%*x %*s %*s %*s %*d", &addr); 55 | if (strstr(line, so_name) != NULL) { 56 | result = addr; 57 | break; 58 | } 59 | } 60 | fclose(fp); 61 | return result; 62 | } 63 | 64 | long get_libc_addr(pid_t pid) 65 | { 66 | return get_so_addr(pid, "libc-"); 67 | } 68 | 69 | long get_libdl_addr(pid_t pid) 70 | { 71 | return get_so_addr(pid, "libdl-"); 72 | } 73 | 74 | int check_loaded(pid_t pid, char *libname) 75 | { 76 | return get_so_addr(pid, libname) != 0; 77 | } 78 | 79 | long get_function_addr(char *so_name, char *func_name) 80 | { 81 | void *so_addr = dlopen(so_name, RTLD_LAZY); 82 | void *func_addr = dlsym(so_addr, func_name); 83 | 84 | return (long)func_addr; 85 | } 86 | -------------------------------------------------------------------------------- /Makefile.include: -------------------------------------------------------------------------------- 1 | #-*- mode: makefile -*- 2 | ifneq ($(findstring $(MAKEFLAGS),s),s) 3 | ifneq ($(V),1) 4 | QUIET_CC = @echo ' CC '$(patsubst $(objdir)/%,%,$@); 5 | QUIET_CC_FPIC = @echo ' CC FPIC '$(patsubst $(objdir)/%,%,$@); 6 | QUIET_AR = @echo ' AR '$(patsubst $(objdir)/%,%,$@); 7 | QUIET_ASM = @echo ' ASM '$(patsubst $(objdir)/%,%,$@); 8 | QUIET_LINK = @echo ' LINK '$(patsubst $(objdir)/%,%,$@); 9 | QUIET_MKDIR = @echo ' MKDIR '$(patsubst $(objdir)/%,%,$@); 10 | QUIET_GEN = @echo ' GEN '$(patsubst $(objdir)/%,%,$@); 11 | QUIET_FLEX = @echo ' FLEX '$@; 12 | QUIET_BISON = @echo ' BISON '$@; 13 | 14 | QUIET_CLEAN = @printf ' CLEAN %s\n' $1; 15 | QUIET_INSTALL = @printf ' INSTALL %s\n' $1; 16 | QUIET_UNINSTALL= @printf ' REMOVE %s\n' $1; 17 | 18 | Q = @ 19 | endif 20 | endif 21 | 22 | RM = rm -f 23 | INSTALL = install 24 | 25 | uname_M := $(shell uname -m 2>/dev/null || echo not) 26 | 27 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/arm.*/arm/ ) 28 | ifeq ($(ARCH),x86_64) 29 | ifneq ($(findstring m32,$(CFLAGS)),) 30 | override ARCH := i386 31 | endif 32 | endif 33 | 34 | 35 | # Makefiles suck: This macro sets a default value of $(2) for the 36 | # variable named by $(1), unless the variable has been set by 37 | # environment or command line. This is necessary for CC and AR 38 | # because make sets default values, so the simpler ?= approach 39 | # won't work as expected. 40 | define allow-override 41 | $(if $(or $(findstring environment,$(origin $(1))),\ 42 | $(findstring command line,$(origin $(1)))),,\ 43 | $(eval $(1) = $(2))) 44 | endef 45 | 46 | # Allow setting CC and AR and LD, or setting CROSS_COMPILE as a prefix. 47 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) 48 | $(call allow-override,AR,$(CROSS_COMPILE)ar) 49 | $(call allow-override,LD,$(CROSS_COMPILE)ld) 50 | 51 | COMMON_CFLAGS := -D_GNU_SOURCE $(CFLAGS) $(CPPFLAGS) 52 | COMMON_CFLAGS += -iquote $(srcdir) -iquote $(objdir) -iquote $(srcdir)/arch/$(ARCH) 53 | #CFLAGS-DEBUG = -g -D_GNU_SOURCE $(CFLAGS_$@) 54 | COMMON_LDFLAGS := -lrt -ldl -pthread $(LDFLAGS) 55 | 56 | LIB_CFLAGS = $(COMMON_CFLAGS) $(CFLAGS_$@) $(CFLAGS_lib) 57 | LIB_CFLAGS += -fPIC -fvisibility=hidden -fno-omit-frame-pointer 58 | LIB_LDFLAGS = $(COMMON_LDFLAGS) $(LDFLAGS_$@) $(LDFLAGS_lib) -Wl,--no-undefined 59 | 60 | COMMON_CFLAGS += -W -Wall -Wno-unused-parameter -Wno-missing-field-initializers 61 | 62 | ifeq ($(DEBUG), 1) 63 | COMMON_CFLAGS += -O0 -g 64 | else 65 | COMMON_CFLAGS += -O2 -g 66 | endif 67 | 68 | ifeq ($(COVERAGE), 1) 69 | COMMON_CFLAGS += -O0 -g --coverage -U_FORTIFY_SOURCE 70 | LIB_CFLAGS += -O0 -g --coverage -U_FORTIFY_SOURCE 71 | LIB_LDFLAGS += --coverage 72 | endif 73 | 74 | export ARCH RM INSTALL VERSION_GIT CC AR LD 75 | export LIB_CFLAGS LIB_LDFLAGS COMMON_CFLAGS 76 | export dependency-check 77 | -------------------------------------------------------------------------------- /arch/x86_64/inject-content.S: -------------------------------------------------------------------------------- 1 | #include "utils/asm.h" 2 | 3 | .file "inject.c" 4 | .section .data 5 | .globl inject_contents_start 6 | .type inject_contents_start, @function 7 | inject_contents_start: 8 | .size inject_contents_start, .-inject_contents_start 9 | 10 | // inject_so_loader 11 | .text 12 | .globl inject_so_loader 13 | .section .data 14 | .type inject_so_loader, @function 15 | inject_so_loader: 16 | /* 17 | * The PC of the target program is changed by uftrace to reach here 18 | * to make load the shared object module the libmcount. uftrace use 19 | * ptrace to do this. but sometime target program have not jump to PC 20 | * which assigned by uftrace. there is some different between assigned 21 | * address and real jump address. maybe it cause by ptrace inner work. 22 | * 23 | * [NOTICE] 24 | * nop instructions has been added to mitigate this problem. 25 | */ 26 | nop 27 | nop 28 | nop 29 | 30 | /* 31 | * save registers what used in below codes 32 | */ 33 | pushq %rax 34 | pushq %rsi 35 | pushq %rdi 36 | pushq %r9 37 | /* create new stack frame to save registers */ 38 | pushq %rsp 39 | pushq %rbp 40 | movq %rsp, %rbp 41 | 42 | /* 43 | * since recently dlopen use movabs instruction, 44 | * stack must be aligned by 16byte. 45 | */ 46 | andq $-16, %rsp 47 | 48 | /* 49 | * 1st argument to dlopen(): 50 | * absolute path of shared object to loaded. 51 | */ 52 | leaq inject_so_path(%rip), %rdi 53 | /* 54 | * 2nd argument 55 | * 0x80000000 : __RTLD_DLOPEN 56 | * 0x00000001 : RTLD_NOW 57 | */ 58 | movabs $0x80000001, %rsi 59 | movq inject_dlopen_addr(%rip), %r9 60 | 61 | /* 62 | * call dlopen() 63 | */ 64 | callq *%r9 65 | 66 | /* 67 | * rewind stack 68 | */ 69 | movq %rbp, %rsp 70 | popq %rbp 71 | popq %rsp 72 | /* 73 | * restore used registers. 74 | */ 75 | popq %r9 76 | popq %rdi 77 | popq %rsi 78 | popq %rax 79 | 80 | /* 81 | * return to original control flow that 82 | * before to trapped by ptrace. 83 | */ 84 | jmpq *0(%rip) 85 | .size inject_so_loader, .-inject_so_loader 86 | // inject_so_ret 87 | .globl inject_so_loader_ret 88 | .section .data 89 | inject_so_loader_ret: 90 | .zero 8 91 | .type inject_so_loader_ret, @object 92 | .size inject_so_loader_ret,8 93 | // inject_dlopen_addr 94 | .globl inject_dlopen_addr 95 | .section .data 96 | inject_dlopen_addr: 97 | .zero 8 98 | .type inject_dlopen_addr, @object 99 | .size inject_dlopen_addr,8 100 | // inject_so_path 101 | .globl inject_so_path 102 | .section .data 103 | .type inject_so_path, @object 104 | .size inject_so_path, 128 105 | inject_so_path: 106 | .string "absolute path of shared object" 107 | .zero 101 108 | 109 | .globl inject_contents_end 110 | .section .data 111 | .type inject_contents_end, @function 112 | inject_contents_end: 113 | .size inject_contents_end, .-inject_contents_end 114 | 115 | -------------------------------------------------------------------------------- /utils/ptrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ptrace.h" 10 | #include "utils.h" 11 | 12 | void ptrace_attach(pid_t target) 13 | { 14 | int status; 15 | 16 | if (ptrace(PTRACE_ATTACH, target, NULL, NULL) == -1) 17 | pr_err("ptrace(PTRACE_ATTACH) failed"); 18 | 19 | if (waitpid(target, &status, WUNTRACED) != target) 20 | pr_err("waitpid(%d) failed", target); 21 | } 22 | 23 | void ptrace_detach(pid_t target) 24 | { 25 | if (ptrace(PTRACE_DETACH, target, NULL, NULL) == -1) 26 | pr_err("ptrace(PTRACE_DETACH) failed"); 27 | } 28 | 29 | void ptrace_getregs(pid_t target, struct REG_TYPE *regs) 30 | { 31 | if (ptrace(PTRACE_GETREGS, target, NULL, regs) == -1) 32 | pr_err("ptrace(PTRACE_GETREGS) failed"); 33 | } 34 | 35 | void ptrace_cont(pid_t target) 36 | { 37 | struct timespec sleeptime = {2, 00000000}; 38 | 39 | if (ptrace(PTRACE_CONT, target, NULL, NULL) == -1) 40 | pr_err("ptrace(PTRACE_CONT) failed"); 41 | 42 | /* 43 | * need enough time to finish libmount startup. 44 | * this will need more time if user use verbose option like '-vvv'. 45 | */ 46 | nanosleep(&sleeptime, NULL); 47 | // make sure the target process received SIGTRAP after stopping. 48 | check_target_sig(target); 49 | } 50 | 51 | void ptrace_setregs(pid_t target, struct REG_TYPE *regs) 52 | { 53 | if (ptrace(PTRACE_SETREGS, target, NULL, regs) == -1) 54 | pr_err("ptrace(PTRACE_SETREGS) failed"); 55 | } 56 | 57 | void ptrace_getsiginfo(pid_t target, siginfo_t *targetsig) 58 | { 59 | if (ptrace(PTRACE_GETSIGINFO, target, NULL, targetsig) == -1) 60 | pr_err("ptrace(PTRACE_GETSIGINFO) failed"); 61 | } 62 | 63 | void ptrace_read(int pid, unsigned long addr, void *vptr, int len) 64 | { 65 | int bytesRead = 0; 66 | int i = 0; 67 | long word = 0; 68 | long *ptr = (long *) vptr; 69 | 70 | while (bytesRead < len) { 71 | word = ptrace(PTRACE_PEEKTEXT, pid, addr + bytesRead, NULL); 72 | if (word == -1) { 73 | pr_err("ptrace(PTRACE_PEEKTEXT) failed %lx", 74 | addr + bytesRead, bytesRead); 75 | } 76 | bytesRead += sizeof(word); 77 | ptr[i++] = word; 78 | } 79 | } 80 | 81 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len) 82 | { 83 | int byteCount = 0; 84 | long word = 0; 85 | 86 | while (byteCount < len) { 87 | memcpy(&word, vptr + byteCount, sizeof(word)); 88 | word = ptrace(PTRACE_POKETEXT, pid, addr + byteCount, word); 89 | if (word == -1) 90 | pr_err("ptrace(PTRACE_POKETEXT) failed"); 91 | byteCount += sizeof(word); 92 | } 93 | } 94 | 95 | void check_target_sig(int pid) 96 | { 97 | siginfo_t targetsig; 98 | 99 | // check the signal that the child stopped with. 100 | ptrace_getsiginfo(pid, &targetsig); 101 | 102 | /* 103 | * if it wasn't SIGTRAP, then something bad happened 104 | * (most likely a segfault). 105 | */ 106 | if (targetsig.si_signo != SIGTRAP) { 107 | pr_dbg("instead of expected SIGTRAP, target stopped with " 108 | "signal %d: %s\n", targetsig.si_signo, 109 | strsignal(targetsig.si_signo)); 110 | pr_dbg("sending process %d a SIGSTOP signal for debugging " 111 | "purposes\n", pid); 112 | ptrace(PTRACE_CONT, pid, NULL, SIGSTOP); 113 | pr_err("EXIT"); 114 | } 115 | } 116 | 117 | void restore_state_and_detach(pid_t target, unsigned long addr, void *backup, 118 | int datasize, struct REG_TYPE oldregs) 119 | { 120 | ptrace_setregs(target, &oldregs); 121 | ptrace_detach(target); 122 | } 123 | -------------------------------------------------------------------------------- /epoll_test.c: -------------------------------------------------------------------------------- 1 | //https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAXEVENTS 8 14 | 15 | static int make_socket_non_blocking (int sfd) 16 | { 17 | int flags, s; 18 | 19 | flags = fcntl (sfd, F_GETFL, 0); 20 | if (flags == -1) 21 | { 22 | perror ("fcntl"); 23 | return -1; 24 | } 25 | 26 | flags |= O_NONBLOCK; 27 | s = fcntl (sfd, F_SETFL, flags); 28 | if (s == -1) 29 | { 30 | perror ("fcntl"); 31 | return -1; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | static int create_and_bind (char *port) 38 | { 39 | struct addrinfo hints; 40 | struct addrinfo *result, *rp; 41 | int s, sfd; 42 | 43 | memset (&hints, 0, sizeof (struct addrinfo)); 44 | hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ 45 | hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ 46 | hints.ai_flags = AI_PASSIVE; /* All interfaces */ 47 | 48 | s = getaddrinfo (NULL, port, &hints, &result); 49 | if (s != 0) 50 | { 51 | fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s)); 52 | return -1; 53 | } 54 | 55 | for (rp = result; rp != NULL; rp = rp->ai_next) 56 | { 57 | sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol); 58 | if (sfd == -1) 59 | continue; 60 | 61 | s = bind (sfd, rp->ai_addr, rp->ai_addrlen); 62 | if (s == 0) 63 | { 64 | /* We managed to bind successfully! */ 65 | break; 66 | } 67 | 68 | close (sfd); 69 | } 70 | 71 | if (rp == NULL) 72 | { 73 | fprintf (stderr, "Could not bind\n"); 74 | return -1; 75 | } 76 | 77 | freeaddrinfo (result); 78 | 79 | return sfd; 80 | } 81 | 82 | int 83 | main (int argc, char *argv[]) 84 | { 85 | int sfd, s; 86 | int efd; 87 | struct epoll_event event; 88 | struct epoll_event *events; 89 | 90 | if (argc != 2) 91 | { 92 | fprintf (stderr, "Usage: %s [port]\n", argv[0]); 93 | exit (EXIT_FAILURE); 94 | } 95 | 96 | sfd = create_and_bind (argv[1]); 97 | if (sfd == -1) 98 | abort (); 99 | 100 | s = make_socket_non_blocking (sfd); 101 | if (s == -1) 102 | abort (); 103 | 104 | s = listen (sfd, SOMAXCONN); 105 | if (s == -1) 106 | { 107 | perror ("listen"); 108 | abort (); 109 | } 110 | 111 | efd = epoll_create1 (0); 112 | if (efd == -1) 113 | { 114 | perror ("epoll_create"); 115 | abort (); 116 | } 117 | 118 | event.data.fd = sfd; 119 | event.events = EPOLLIN | EPOLLET; 120 | s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event); 121 | if (s == -1) 122 | { 123 | perror ("epoll_ctl"); 124 | abort (); 125 | } 126 | 127 | /* Buffer where events are returned */ 128 | events = calloc (MAXEVENTS, sizeof event); 129 | 130 | /* The event loop */ 131 | while (1) 132 | { 133 | int n, i; 134 | 135 | n = epoll_wait (efd, events, MAXEVENTS, -1); 136 | for (i = 0; i < n; i++) 137 | { 138 | if ((events[i].events & EPOLLERR) || 139 | (events[i].events & EPOLLHUP) || 140 | (!(events[i].events & EPOLLIN))) 141 | { 142 | /* An error has occured on this fd, or the socket is not 143 | ready for reading (why were we notified then?) */ 144 | fprintf (stderr, "epoll error\n"); 145 | if(events[i].events & EPOLLERR) printf("EPOLLERR\n"); 146 | if(events[i].events & EPOLLHUP) printf("EPOLLHUP\n"); 147 | 148 | close (events[i].data.fd); 149 | continue; 150 | } 151 | 152 | else if (sfd == events[i].data.fd) 153 | { 154 | /* We have a notification on the listening socket, which 155 | means one or more incoming connections. */ 156 | while (1) 157 | { 158 | struct sockaddr in_addr; 159 | socklen_t in_len; 160 | int infd; 161 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 162 | 163 | in_len = sizeof in_addr; 164 | infd = accept (sfd, &in_addr, &in_len); 165 | if (infd == -1) 166 | { 167 | if ((errno == EAGAIN) || 168 | (errno == EWOULDBLOCK)) 169 | { 170 | /* We have processed all incoming 171 | connections. */ 172 | break; 173 | } 174 | else 175 | { 176 | perror ("accept"); 177 | break; 178 | } 179 | } 180 | 181 | s = getnameinfo (&in_addr, in_len, 182 | hbuf, sizeof hbuf, 183 | sbuf, sizeof sbuf, 184 | NI_NUMERICHOST | NI_NUMERICSERV); 185 | if (s == 0) 186 | { 187 | printf("Accepted connection on descriptor %d " 188 | "(host=%s, port=%s)\n", infd, hbuf, sbuf); 189 | } 190 | 191 | /* Make the incoming socket non-blocking and add it to the 192 | list of fds to monitor. */ 193 | s = make_socket_non_blocking (infd); 194 | if (s == -1) 195 | abort (); 196 | 197 | event.data.fd = infd; 198 | event.events = EPOLLIN | EPOLLET; 199 | s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event); 200 | if (s == -1) 201 | { 202 | perror ("epoll_ctl"); 203 | abort (); 204 | } 205 | } 206 | continue; 207 | } 208 | else 209 | { 210 | /* We have data on the fd waiting to be read. Read and 211 | display it. We must read whatever data is available 212 | completely, as we are running in edge-triggered mode 213 | and won't get a notification again for the same 214 | data. */ 215 | int done = 0; 216 | 217 | while (1) 218 | { 219 | ssize_t count; 220 | char buf[512]; 221 | 222 | count = read (events[i].data.fd, buf, sizeof buf); 223 | if (count == -1) 224 | { 225 | /* If errno == EAGAIN, that means we have read all 226 | data. So go back to the main loop. */ 227 | if (errno != EAGAIN) 228 | { 229 | perror ("read"); 230 | done = 1; 231 | } 232 | break; 233 | } 234 | else if (count == 0) 235 | { 236 | /* End of file. The remote has closed the 237 | connection. */ 238 | done = 1; 239 | break; 240 | } 241 | 242 | /* Write the buffer to standard output */ 243 | s = write (1, buf, count); 244 | if (s == -1) 245 | { 246 | perror ("write"); 247 | abort (); 248 | } 249 | } 250 | 251 | if (done) 252 | { 253 | printf ("Closed connection on descriptor %d\n", 254 | events[i].data.fd); 255 | 256 | /* Closing the descriptor will make epoll remove it 257 | from the set of descriptors which are monitored. */ 258 | close (events[i].data.fd); 259 | } 260 | } 261 | } 262 | } 263 | 264 | free (events); 265 | 266 | close (sfd); 267 | 268 | return EXIT_SUCCESS; 269 | } 270 | 271 | -------------------------------------------------------------------------------- /arch/x86_64/inject.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux module for injecting shared object to the process by using ptrace. 3 | * 4 | * copied from: https://github.com/gaffe23/linux-inject 5 | * Released under the GPL v2+. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "utils/inject.h" 17 | #include "utils/ptrace.h" 18 | #include "utils/utils.h" 19 | 20 | #define SO_LIBC_NAME "libc.so.6" 21 | #define LIBC_DLOPEN_NAME "__libc_dlopen_mode" 22 | 23 | #define MAX_PATH_LENGTH 128 24 | #define MAX_THREAD_COUNT 64 25 | 26 | #define WORD_SIZE sizeof(long) 27 | #define WORD_ALIGN(x) ALIGN(x, WORD_SIZE) 28 | 29 | // following externals are inside of inject-content.S 30 | extern void inject_so_loader(void); 31 | extern char inject_so_path[128]; 32 | extern long long inject_so_loader_ret; 33 | extern long long inject_dlopen_addr; 34 | extern const void inject_contents_start; 35 | extern const void inject_contents_end; 36 | 37 | unsigned int get_tids(pid_t pid, pid_t **tids) 38 | { 39 | DIR *d = NULL; 40 | struct dirent *dir; 41 | char path[64] = {0,}; 42 | char strpid[10] = {0,}; 43 | unsigned int thread_count = 0; 44 | 45 | snprintf(strpid, sizeof(strpid), "%d", pid); 46 | snprintf(path, sizeof(path), "/proc/%d/task/", pid); 47 | d = opendir(path); 48 | *tids = xcalloc(sizeof(pid_t), MAX_THREAD_COUNT); 49 | 50 | if (d) { 51 | while ((dir = readdir(d)) != NULL) { 52 | if (!strncmp(dir->d_name, ".", 1)) { 53 | continue; 54 | } else if (!strncmp(dir->d_name, "..", 2)) { 55 | continue; 56 | } else if (!strncmp(dir->d_name, strpid, 57 | strlen(strpid))) { 58 | continue; 59 | } 60 | (*tids)[thread_count++] = atoi(dir->d_name); 61 | } 62 | closedir(d); 63 | } 64 | return thread_count; 65 | } 66 | 67 | /* 68 | * While injecting a shared object, attaching to all the child threads 69 | * to puts them into a suspended state. 70 | */ 71 | void suspend_child_threads(pid_t pid, uintptr_t injected_addr, 72 | pid_t **tids, uint32_t thread_count) 73 | { 74 | pid_t tid; 75 | uint32_t index; 76 | 77 | for (index=0;index < thread_count;index++) { 78 | tid = (*tids)[index]; 79 | ptrace_attach(tid); 80 | } 81 | } 82 | 83 | void release_child_threads(pid_t pid, uintptr_t injected_addr, 84 | pid_t **tids, uint32_t thread_count) 85 | { 86 | pid_t tid; 87 | uint32_t index; 88 | 89 | for (index=0;index < thread_count;index++) { 90 | tid = (*tids)[index]; 91 | ptrace_detach(tid); 92 | } 93 | } 94 | 95 | 96 | int inject(char *libname, pid_t pid) 97 | { 98 | struct user_regs_struct oldregs, regs; 99 | int lib_path_length, mypid; 100 | unsigned int thread_count; 101 | long my_libc_addr; 102 | long target_lib_addr, dlopen_addr, dlopen_offset, addr; 103 | long target_dlopen_addr; 104 | char *injected_area; 105 | char *lib_path = NULL; 106 | pid_t target = 0; 107 | // array of child thread ids. 108 | pid_t *tids = NULL; 109 | 110 | /* 111 | * figure out the size of contents to be injected. 112 | * so, we know how big of a buffer to allocate. 113 | */ 114 | uintptr_t inject_end = (uintptr_t)&inject_contents_end; 115 | uintptr_t inject_begin = (uintptr_t)&inject_contents_start; 116 | uint32_t inject_size = inject_end - inject_begin + 1; 117 | pr_dbg("size of code to inject %d\n", inject_size); 118 | 119 | /* 120 | * Ptrace reads the word size at once. so, make sure that 121 | * the space to inject is large enough to be read from ptrace. 122 | */ 123 | inject_size = WORD_ALIGN(inject_size); 124 | 125 | lib_path = realpath(libname, NULL); 126 | if (!lib_path) 127 | pr_err("can't find file %s", libname); 128 | 129 | target = pid; 130 | lib_path_length = strlen(lib_path) + 1; 131 | mypid = getpid(); 132 | 133 | my_libc_addr = get_libc_addr(mypid); 134 | dlopen_addr = get_function_addr(SO_LIBC_NAME, LIBC_DLOPEN_NAME); 135 | dlopen_offset = dlopen_addr - my_libc_addr; 136 | target_lib_addr = get_libc_addr(target); 137 | 138 | /* 139 | * get the target process' libdl address and use it to find the 140 | * address of the dlopen we want to use inside the target process 141 | */ 142 | 143 | if (target_lib_addr == 0) 144 | pr_err("Cannot inject library if target process does not load libdl.so"); 145 | 146 | target_dlopen_addr = target_lib_addr + dlopen_offset; 147 | 148 | memset(&oldregs, 0, sizeof(struct user_regs_struct)); 149 | memset(®s, 0, sizeof(struct user_regs_struct)); 150 | ptrace_attach(target); 151 | ptrace_getregs(target, &oldregs); 152 | memcpy(®s, &oldregs, sizeof(struct user_regs_struct)); 153 | 154 | inject_dlopen_addr = target_dlopen_addr; 155 | memset(inject_so_path, 0x0, sizeof(inject_so_path)-1); 156 | memcpy(inject_so_path, lib_path, lib_path_length); 157 | 158 | pr_dbg("Path of Shared object to be injected : %s\n", inject_so_path); 159 | 160 | /* 161 | * since ELF align each section size by paging size, many section 162 | * have padding at its tail. code be load shared object is 163 | * injected to here. 164 | */ 165 | addr = get_inject_code_addr(target) - inject_size; 166 | 167 | inject_so_loader_ret = regs.rip; 168 | pr_dbg("Return address : %llx\n", inject_so_loader_ret); 169 | /* 170 | * now that we have an address to copy code to, set the target's 171 | * rip to it. we have to advance by 2 bytes here because rip gets 172 | * incremented by the size of the current instruction, and the 173 | * instruction at the start of the function to inject always 174 | * happens to be 2 bytes long. 175 | */ 176 | regs.rip = addr + 2; 177 | pr_dbg("RIP Register : %llx\n", regs.rip); 178 | 179 | ptrace_setregs(target, ®s); 180 | 181 | /* 182 | * set up a buffer to hold the code we're going to inject 183 | * into the target process. 184 | */ 185 | injected_area = xmalloc(inject_size); 186 | memset(injected_area, 0, inject_size); 187 | 188 | // copy the contents of inject_contents to a buffer. 189 | memcpy(injected_area, &inject_contents_start, inject_size - 1); 190 | 191 | /* 192 | * copy inject_so_loader inner assemly code to the target address 193 | * inside the target process' address space. 194 | */ 195 | ptrace_write(target, addr, injected_area, inject_size); 196 | 197 | /* 198 | * ptrace continue will make child thread get wake-up. 199 | * this can be harmful during injection. 200 | * therefore, make all child threads enter the loop 201 | * until injecting done. 202 | */ 203 | thread_count = get_tids(pid, &tids); 204 | suspend_child_threads(target, addr, &tids, thread_count); 205 | 206 | /* 207 | * now that the new code is in place, let the target run 208 | * our injected code. 209 | */ 210 | ptrace_detach(target); 211 | release_child_threads(target, addr, &tids, thread_count); 212 | 213 | return 0; 214 | } 215 | --------------------------------------------------------------------------------