├── .gitignore ├── architecture.png ├── gdbinit.py ├── LICENSE ├── src ├── main.c ├── debug-server.h ├── arg.c ├── log.c ├── init.c ├── sig.c ├── tracer.c ├── service.c └── command.c ├── Makefile ├── README.md ├── exp.py └── gdbpwn.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /release/ 3 | /.vscode/ 4 | /build.sh 5 | -------------------------------------------------------------------------------- /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ex-Origin/debug-server/HEAD/architecture.png -------------------------------------------------------------------------------- /gdbinit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import shutil 5 | 6 | if __name__ == '__main__': 7 | 8 | if(os.access('./exp.py', os.F_OK) == 0): 9 | shutil.copy(os.path.join(os.path.dirname(os.path.abspath(os.readlink(__file__))), 'exp.py'), './exp.py') 10 | os.chmod('./exp.py', 0o755) 11 | 12 | if(os.access('./debug-server', os.F_OK) == 0): 13 | shutil.copy(os.path.join(os.path.dirname(os.path.abspath(os.readlink(__file__))), 'release/debug-server.x86_64'), './debug-server') 14 | os.chmod('./debug-server', 0o755) 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ex-Origin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "debug-server.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | int run, event_num, i; 9 | struct epoll_event events[0x10]; 10 | 11 | parsing_argv(argc, argv); 12 | tty_init(); 13 | init_fd(); 14 | 15 | info_printf("Start debugging service, pid=%d, version=%s\n", getpid(), VERSION); 16 | run = 1; 17 | while(run) 18 | { 19 | event_num = epoll_wait(epoll_fd, events, sizeof(events)/sizeof(events[0]), -1); 20 | for(i = 0; i < event_num; i++) 21 | { 22 | if(events[i].data.fd == command_socket) 23 | { 24 | run = command_handler(); 25 | } 26 | else if(events[i].data.fd == service_socket) 27 | { 28 | run = service_handler(); 29 | } 30 | else if(events[i].data.fd == signal_fd) 31 | { 32 | run = signal_handler(); 33 | } 34 | else if(events[i].data.fd == gdbserver_pipe[0]) 35 | { 36 | run = gdbserver_pipe_handler(); 37 | } 38 | else if(events[i].data.fd == strace_pipe[0]) 39 | { 40 | run = strace_pipe_handler(); 41 | } 42 | } 43 | } 44 | 45 | disconnect_gdb(); 46 | 47 | info_printf("Exit debugging service, pid=%d\n", getpid()); 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET_EXEC := debug-server 3 | 4 | BUILD_DIR := ./build 5 | SRC_DIRS := ./src 6 | 7 | # Find all the C and C++ files we want to compile 8 | # Note the single quotes around the * expressions. The shell will incorrectly expand these otherwise, but we want to send the * directly to the find command. 9 | SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s') 10 | 11 | # Prepends BUILD_DIR and appends .o to every src file 12 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 13 | 14 | # String substitution (suffix version without %). 15 | DEPS := $(OBJS:.o=.d) 16 | 17 | # Every folder in ./src will need to be passed to GCC so that it can find header files 18 | INC_DIRS := $(shell find $(SRC_DIRS) -type d) 19 | # Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag 20 | INC_FLAGS := $(addprefix -I,$(INC_DIRS)) 21 | 22 | # The -MMD and -MP flags together generate Makefiles for us! 23 | # These files will have .d instead of .o as the output. 24 | CFLAGS := $(INC_FLAGS) -MMD -MP -O3 25 | LDFLAGS := -static 26 | 27 | # The final build step. 28 | $(BUILD_DIR)/$(TARGET_EXEC): $(OBJS) 29 | $(CC) $(OBJS) -o $@ $(LDFLAGS) 30 | 31 | # Build step for C source 32 | $(BUILD_DIR)/%.c.o: %.c 33 | mkdir -p $(dir $@) 34 | $(CC) $(CFLAGS) $(CFLAGS) -c $< -o $@ 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -r $(BUILD_DIR) 39 | 40 | # Include the .d makefiles. The - at the front suppresses the errors of missing 41 | # Makefiles. Initially, all the .d files will be missing, and we don't want those 42 | # errors to show up. 43 | -include $(DEPS) 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Debug Server 3 | 4 | A customized debug tool designing to automatic attach the target process in either remote or pwntools contexts. 5 | 6 | ![Architecture](architecture.png) 7 | 8 | ## Requirements 9 | 10 | Please install the following package in remote environment. 11 | 12 | ```shell 13 | apt update 14 | apt install -y gdbserver strace 15 | ``` 16 | 17 | Please install the following package in local environment. 18 | 19 | ```shell 20 | apt update 21 | apt install -y gdb-multiarch 22 | ``` 23 | 24 | ## Usage 25 | 26 | 1. Add this directory to `$PATH`. 27 | 2. Run `gdbinit.py` in your intended workspace to initial environment. 28 | 3. Run `debug-server` 29 | 30 | ```shell 31 | Usage: debug-server [-hmsvn] [-e CMD] [-p PID] [-o CMD] 32 | 33 | General: 34 | -e CMD service argv 35 | -p PID attach to PID 36 | -o CMD get pid by popen 37 | -h print help message 38 | -m enable multi-service 39 | -s halt at entry point 40 | -v show debug information 41 | -n disable address space randomization 42 | -u do not limit memory 43 | -6 ipv6 mode 44 | ``` 45 | 46 | 4. Use `gdbpwn.py` to connect to the target IP. 47 | 5. Use `exp.py` to connect to the target IP to start the target and send `attach instruction`. 48 | 49 | https://github.com/Ex-Origin/debug-server/assets/32574109/af389470-8e71-4432-b6ce-edff9ea65e34 50 | 51 | ## Features 52 | 53 | * Cross-platform 54 | * More automatic 55 | * Streamline the debugging process for reverse engineering (Feature: HALT_AT_ENTRY_POINT) 56 | -------------------------------------------------------------------------------- /exp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding:utf-8 -*- 3 | 4 | from pwn import * 5 | context.clear(arch='amd64', os='linux', log_level='debug') 6 | 7 | attach_host = os.getenv('TARGET_SERVER_IP') 8 | attach_port = 9545 9 | def attach(script=''): 10 | tmp_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 11 | gdb_script = re.sub(r'#.*', '', 12 | f''' 13 | define pr 14 | x/16gx $rebase(0x0) 15 | end 16 | 17 | b *$rebase(0x0) 18 | ''' + '\n' + script) 19 | gdbinit = '/tmp/gdb_script_' + attach_host 20 | script_f = open(gdbinit, 'w') 21 | script_f.write(gdb_script) 22 | script_f.close() 23 | _attach_host = attach_host 24 | if attach_host.find(':') == -1: _attach_host = '::ffff:' + attach_host 25 | tmp_sock.sendto(struct.pack('BB', 0x02, len(gdbinit.encode())) + gdbinit.encode(), (_attach_host, attach_port)) 26 | tmp_sock.recvfrom(4096) 27 | tmp_sock.close() 28 | print('attach successfully') 29 | def strace(): 30 | tmp_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP 31 | _attach_host = attach_host 32 | if attach_host.find(':') == -1: _attach_host = '::ffff:' + attach_host 33 | tmp_sock.sendto(struct.pack('B', 0x03), (_attach_host, attach_port)) 34 | tmp_sock.recvfrom(4096) 35 | tmp_sock.close() 36 | print('strace successfully') 37 | def address(search:str)->int: 38 | tmp_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 39 | _attach_host = attach_host 40 | if attach_host.find(':') == -1: _attach_host = '::ffff:' + attach_host 41 | tmp_sock.sendto(struct.pack('BB', 0x04, len(search.encode())) + search.encode(), (_attach_host, attach_port)) 42 | tmp_recv = tmp_sock.recvfrom(4096)[0] 43 | tmp_sock.close() 44 | return struct.unpack('Q', tmp_recv[2:10])[0] 45 | def run_service(): 46 | tmp_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP 47 | _attach_host = attach_host 48 | if attach_host.find(':') == -1: _attach_host = '::ffff:' + attach_host 49 | tmp_sock.sendto(struct.pack('B', 0x06), (_attach_host, attach_port)) 50 | tmp_sock.recvfrom(4096) 51 | tmp_sock.close() 52 | print('run_service successfully') 53 | 54 | sh = remote(attach_host, 9541) 55 | attach() 56 | 57 | sh.interactive() 58 | -------------------------------------------------------------------------------- /src/debug-server.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_DEBUG_SERVER_ 2 | #define _H_DEBUG_SERVER_ 3 | 4 | #define SERVICE_PORT 9541 5 | #define COMMAND_PORT 9545 6 | #define GDBSERVER_PORT 9549 7 | 8 | #define VERSION "1.5.2" 9 | 10 | #define COMMAND_GDB_REGISTER 0x01 11 | #define COMMAND_GDBSERVER_ATTACH 0x02 12 | #define COMMAND_STRACE_ATTACH 0x03 13 | #define COMMAND_GET_ADDRESS 0x04 14 | #define COMMAND_GDB_LOGOUT 0x05 15 | #define COMMAND_RUN_SERVICE 0x06 16 | 17 | #define OK_GREEN "\033[92m" 18 | #define WARNING_YELLOW "\033[93m" 19 | #define FAIL_RED "\033[91m" 20 | #define END_CLEAN "\033[0m" 21 | #define GDB_COLOR "\033[96m" 22 | #define STRACE_COLOR "\033[95m" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | /** 29 | * The value must be TRUE, or the program will break down. 30 | * e.g., the value is thing what the program need to do. 31 | **/ 32 | #define CHECK(value) \ 33 | { \ 34 | if ((value) == 0) \ 35 | { \ 36 | error_printf("%s %s:%d\n", strerror(errno), __FILE__, __LINE__); \ 37 | exit(EXIT_FAILURE); \ 38 | } \ 39 | } 40 | 41 | int tty_init(); 42 | int debug_printf(const char *format, ...); 43 | int info_printf(const char *format, ...); 44 | int warning_printf(const char *format, ...); 45 | int error_printf(const char *format, ...); 46 | int gdbserver_output(char *msg); 47 | int strace_output(char *msg); 48 | 49 | extern int arg_opt_e; 50 | extern int arg_opt_p; 51 | extern int arg_opt_o; 52 | extern int arg_opt_m; 53 | // halt at entry point 54 | extern int arg_opt_s; 55 | extern int arg_opt_v; 56 | extern int arg_opt_n; 57 | extern int arg_opt_u; 58 | extern int arg_opt_6; 59 | extern char **arg_execve_argv; 60 | extern char *arg_popen; 61 | extern int arg_pid; 62 | 63 | int parsing_argv(int argc, char *argv[]); 64 | 65 | extern int stopped; 66 | 67 | int close_fd(); 68 | 69 | #include 70 | 71 | extern int service_socket; 72 | extern int command_socket; 73 | extern int signal_fd; 74 | extern int epoll_fd; 75 | extern int gdbserver_pipe[2]; 76 | extern int strace_pipe[2]; 77 | 78 | extern struct sockaddr_in gdb_client_address4; 79 | extern struct sockaddr_in6 gdb_client_address6; 80 | 81 | int command_handler(); 82 | int service_handler(); 83 | int signal_handler(); 84 | int gdbserver_pipe_handler(); 85 | int strace_pipe_handler(); 86 | int disconnect_gdb(); 87 | int start_service(int client_sock); 88 | 89 | int init_fd(); 90 | 91 | #include 92 | 93 | extern int service_pid; 94 | extern int gdbserver_pid; 95 | extern int strace_pid; 96 | 97 | extern sigset_t old_mask; 98 | 99 | int gdbserver_attach_pid(int pid); 100 | int strace_attach_pid(int pid); 101 | 102 | #endif -------------------------------------------------------------------------------- /gdbpwn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import socket 6 | import struct 7 | import signal 8 | import time 9 | import logging 10 | 11 | command_port = 9545 12 | gdb_port = 9549 13 | gdb_pid = -1 14 | 15 | COMMAND_GDB_REGISTER = 0x01 16 | COMMAND_GDBSERVER_ATTACH = 0x02 17 | COMMAND_GDB_LOGOUT = 0x05 18 | 19 | last_int = 0 20 | def int_handler(signum, frame): 21 | global last_int 22 | tmp = time.time() 23 | if(tmp - last_int < 0.2): 24 | if(gdb_pid != -1): 25 | os.kill(gdb_pid, signal.SIGTERM) 26 | os.waitpid(gdb_pid, os.WNOHANG) 27 | exit(0) 28 | else: 29 | last_int = tmp 30 | 31 | def term_handler(signum, frame): 32 | if(gdb_pid != -1): 33 | os.kill(gdb_pid, signal.SIGTERM) 34 | os.waitpid(gdb_pid, os.WNOHANG) 35 | exit(0) 36 | 37 | if __name__ == '__main__': 38 | if(len(sys.argv) < 2): 39 | server_ip = os.getenv('TARGET_SERVER_IP') 40 | if not server_ip: 41 | print(f'Usage: {sys.argv[0]} [IP]', file=sys.stderr) 42 | exit(1) 43 | else: 44 | server_ip = sys.argv[1] 45 | 46 | logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) 47 | 48 | command_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP 49 | if server_ip.find(':') == -1: 50 | logging.info(f'Connecting to {server_ip}:{command_port}') 51 | server_ip = '::ffff:' + server_ip 52 | else: 53 | logging.info(f'Connecting to [{server_ip}]:{command_port}') 54 | command_sock.sendto(struct.pack('B', COMMAND_GDB_REGISTER), (server_ip, command_port)) 55 | data, address = command_sock.recvfrom(4096) 56 | logging.info(f'It has connected successfully') 57 | signal.signal(signal.SIGINT, int_handler) 58 | signal.signal(signal.SIGTERM, term_handler) 59 | logging.info(f'Start gdb client') 60 | while(True): 61 | data, address = command_sock.recvfrom(4096) 62 | option = struct.unpack('B', data[:1])[0] 63 | if option == COMMAND_GDBSERVER_ATTACH: 64 | logging.info(f'Receive COMMAND_GDBSERVER_ATTACH') 65 | length = struct.unpack('B', data[1:2])[0] 66 | gdbscript = data[2:2+length].decode() 67 | if(gdb_pid != -1): 68 | print(f"KILL the existed gdb process, pid={gdb_pid}") 69 | os.kill(gdb_pid, signal.SIGTERM) 70 | os.waitpid(gdb_pid, 0) 71 | gdb_pid = -1 72 | 73 | gdb_pid = os.fork() 74 | if(gdb_pid == 0): 75 | command_sock.close() 76 | args = ['/usr/bin/gdb-multiarch', '-q', '-ex', f'target remote {server_ip}:{gdb_port}', '-x', gdbscript] 77 | os.execv(args[0], args) 78 | elif option == COMMAND_GDB_LOGOUT: 79 | logging.info(f'Receive COMMAND_GDB_LOGOUT') 80 | if(gdb_pid != -1): 81 | print(f"KILL the existed gdb process, pid={gdb_pid}") 82 | os.kill(gdb_pid, signal.SIGTERM) 83 | os.waitpid(gdb_pid, 0) 84 | gdb_pid = -1 85 | break 86 | logging.info(f'End gdb client') 87 | -------------------------------------------------------------------------------- /src/arg.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "debug-server.h" 7 | 8 | int arg_opt_e = 0; 9 | int arg_opt_p = 0; 10 | int arg_opt_o = 0; 11 | int arg_opt_m = 0; 12 | int arg_opt_s = 0; 13 | int arg_opt_v = 0; 14 | int arg_opt_n = 0; 15 | int arg_opt_u = 0; 16 | int arg_opt_6 = 0; 17 | 18 | char **arg_execve_argv = NULL; 19 | char *arg_popen = NULL; 20 | int arg_pid = -1; 21 | 22 | char **parsing_execve_str(char *cmd) 23 | { 24 | char *ptr = cmd, **tmp, **argv = NULL; 25 | int argc = 0, max; 26 | int in_single_quote = 0, in_double_quote = 0; 27 | 28 | max = 16; 29 | CHECK((argv = calloc(max, sizeof(char *))) != NULL); 30 | 31 | while (*ptr != '\0') 32 | { 33 | char *arg_start = ptr; 34 | char *arg = malloc(strlen(ptr) + 1); // Temporary buffer to hold the parsed argument 35 | CHECK(arg != NULL); 36 | int arg_len = 0; 37 | 38 | while (*ptr != '\0') 39 | { 40 | if (*ptr == '"' && !in_single_quote) { 41 | in_double_quote = !in_double_quote; 42 | } else if (*ptr == '\'' && !in_double_quote) { 43 | in_single_quote = !in_single_quote; 44 | } else if (*ptr == ' ' && !in_single_quote && !in_double_quote) { 45 | ptr ++; 46 | break; // Space outside quotes means end of argument 47 | } else { 48 | arg[arg_len++] = *ptr; // Add the character to the argument 49 | } 50 | ptr ++; 51 | } 52 | 53 | arg[arg_len] = '\0'; // Null-terminate the argument 54 | 55 | if (argc + 1 > max) 56 | { 57 | CHECK((tmp = realloc(argv, (max * 2) * sizeof(char *))) != NULL); 58 | argv = tmp; 59 | max = max * 2; 60 | } 61 | argv[argc++] = arg; 62 | } 63 | 64 | if (in_single_quote || in_double_quote) { 65 | fprintf(stderr, "Error: Unmatched quote in input string\n"); 66 | exit(EXIT_FAILURE); 67 | } 68 | 69 | argv[argc] = NULL; // Null-terminate the argument list 70 | 71 | return argv; 72 | } 73 | 74 | int help() 75 | { 76 | fprintf(stderr, "Usage: debug-server [-hmsvn] [-e CMD] [-p PID] [-o CMD]\n" 77 | "\n" 78 | "debug-server " VERSION "\n" 79 | "General:\n" 80 | " -e CMD service argv\n" 81 | " -p PID attach to PID\n" 82 | " -o CMD get pid by popen\n" 83 | " -h print help message\n" 84 | " -m enable multi-service\n" 85 | " -s halt at entry point\n" 86 | " -v show debug information\n" 87 | " -n disable address space randomization\n" 88 | " -u do not limit memory\n" 89 | " -6 ipv6 mode\n" 90 | ); 91 | exit(EXIT_FAILURE); 92 | } 93 | 94 | int parsing_argv(int argc, char *argv[]) 95 | { 96 | int opt; 97 | while ((opt = getopt(argc, argv, "e:p:o:hmsvnu6")) != -1) { 98 | switch (opt) { 99 | case 'e': 100 | arg_opt_e = 1; 101 | arg_execve_argv = parsing_execve_str(optarg); 102 | break; 103 | case 'p': 104 | arg_opt_p = 1; 105 | arg_pid = atoi(optarg); 106 | case 'o': 107 | arg_opt_o = 1; 108 | arg_popen = optarg; 109 | break; 110 | case 'h': 111 | help(); 112 | break; 113 | case 'm': 114 | arg_opt_m = 1; 115 | break; 116 | case 's': 117 | arg_opt_s = 1; 118 | break; 119 | case 'v': 120 | arg_opt_v = 1; 121 | break; 122 | case 'n': 123 | arg_opt_n = 1; 124 | break; 125 | case 'u': 126 | arg_opt_u = 1; 127 | break; 128 | case '6': 129 | arg_opt_6 = 1; 130 | break; 131 | default: /* '?' */ 132 | help(); 133 | break; 134 | } 135 | } 136 | if(!(arg_opt_e || arg_opt_p || arg_opt_o)) 137 | { 138 | fprintf(stderr, "debug-server: must have -e CMD or -p PID or -o CMD\n"); 139 | help(); 140 | } 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "debug-server.h" 7 | 8 | int tty = 0; 9 | 10 | int tty_init() 11 | { 12 | int output_mode; 13 | tty = isatty(STDOUT_FILENO); 14 | 15 | if(tty) 16 | { 17 | output_mode = _IONBF; 18 | } 19 | else 20 | { 21 | output_mode = _IOLBF; 22 | } 23 | 24 | setvbuf(stdin, NULL, output_mode, 0); 25 | setvbuf(stdout, NULL, output_mode, 0); 26 | setvbuf(stderr, NULL, output_mode, 0); 27 | 28 | return 0; 29 | } 30 | 31 | int prefix_printf(FILE* fp, char *level) 32 | { 33 | va_list args; 34 | // variables to store the date and time components 35 | int hours, minutes, seconds, day, month, year; 36 | // `time_t` is an arithmetic time type 37 | time_t now = 0; 38 | // localtime converts a `time_t` value to calendar time and 39 | // returns a pointer to a `tm` structure with its members 40 | // filled with the corresponding values 41 | struct tm *local; 42 | size_t result; 43 | 44 | now = time(NULL); 45 | #ifdef TIME_OFFSET 46 | now = now + (TIME_OFFSET); 47 | #endif 48 | local = localtime(&now); 49 | 50 | hours = local->tm_hour; // get hours since midnight (0-23) 51 | minutes = local->tm_min; // get minutes passed after the hour (0-59) 52 | seconds = local->tm_sec; // get seconds passed after a minute (0-59) 53 | 54 | day = local->tm_mday; // get day of month (1 to 31) 55 | month = local->tm_mon + 1; // get month of year (0 to 11) 56 | year = local->tm_year + 1900; // get year since 1900 57 | 58 | result = fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d | %-7s | ", year, month, day, hours, minutes, seconds, level); 59 | 60 | return result; 61 | } 62 | 63 | int debug_printf(const char *format, ...) 64 | { 65 | va_list args; 66 | size_t result = -1; 67 | 68 | if(arg_opt_v) 69 | { 70 | prefix_printf(stdout, "DEBUG"); 71 | va_start(args, format); 72 | result = vfprintf (stdout, format, args); 73 | va_end (args); 74 | } 75 | 76 | return result; 77 | } 78 | 79 | int info_printf(const char *format, ...) 80 | { 81 | va_list args; 82 | size_t result; 83 | 84 | if(tty) 85 | { 86 | fprintf(stderr, OK_GREEN); 87 | } 88 | prefix_printf(stdout, "INFO"); 89 | va_start(args, format); 90 | result = vfprintf (stdout, format, args); 91 | va_end (args); 92 | if(tty) 93 | { 94 | fprintf(stderr, END_CLEAN); 95 | } 96 | 97 | return result; 98 | } 99 | 100 | int warning_printf(const char *format, ...) 101 | { 102 | va_list args; 103 | size_t result; 104 | 105 | if(tty) 106 | { 107 | fprintf(stderr, WARNING_YELLOW); 108 | } 109 | prefix_printf(stdout, "WARNING"); 110 | va_start(args, format); 111 | result = vfprintf (stdout, format, args); 112 | va_end (args); 113 | if(tty) 114 | { 115 | fprintf(stderr, END_CLEAN); 116 | } 117 | 118 | return result; 119 | } 120 | 121 | int error_printf(const char *format, ...) 122 | { 123 | va_list args; 124 | size_t result; 125 | 126 | if(tty) 127 | { 128 | fprintf(stderr, FAIL_RED); 129 | } 130 | prefix_printf(stderr, "ERROR"); 131 | va_start(args, format); 132 | result = vfprintf (stderr, format, args); 133 | va_end (args); 134 | if(tty) 135 | { 136 | fprintf(stderr, END_CLEAN); 137 | } 138 | 139 | return result; 140 | } 141 | 142 | int gdbserver_output(char *msg) 143 | { 144 | char *ptr = NULL; 145 | int result = 0; 146 | 147 | if(tty) 148 | { 149 | fprintf(stdout, WARNING_YELLOW); 150 | } 151 | 152 | ptr = strtok(msg, "\n"); 153 | while(ptr != NULL) 154 | { 155 | prefix_printf(stdout, "GDB"); 156 | result += fprintf(stdout, "%s\n", ptr); 157 | ptr = strtok(NULL, "\n"); 158 | } 159 | 160 | if(tty) 161 | { 162 | fprintf(stdout, END_CLEAN); 163 | } 164 | 165 | return result; 166 | } 167 | 168 | int strace_output(char *msg) 169 | { 170 | if(tty) 171 | { 172 | return fprintf(stdout, STRACE_COLOR "%s" END_CLEAN, msg); 173 | } 174 | else 175 | { 176 | return fprintf(stdout, "%s", msg); 177 | } 178 | } 179 | 180 | int gdbserver_pipe_handler() 181 | { 182 | char buf[0x100]; 183 | memset(buf, 0, sizeof(buf)); 184 | CHECK(read(gdbserver_pipe[0], buf, sizeof(buf)-1) != -1); 185 | gdbserver_output(buf); 186 | return 1; 187 | } 188 | 189 | int strace_pipe_handler() 190 | { 191 | char buf[0x100]; 192 | memset(buf, 0, sizeof(buf)); 193 | CHECK(read(strace_pipe[0], buf, sizeof(buf)-1) != -1); 194 | strace_output(buf); 195 | return 1; 196 | } -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "debug-server.h" 9 | 10 | int service_socket = -1; 11 | int command_socket = -1; 12 | int signal_fd = -1; 13 | int epoll_fd = -1; 14 | 15 | int gdbserver_pipe[2] = {-1, -1}; 16 | int strace_pipe[2] = {-1, -1}; 17 | 18 | struct sockaddr_in gdb_client_address4 = {0}; 19 | struct sockaddr_in6 gdb_client_address6 = {0}; 20 | 21 | int service_pid = -1; 22 | int gdbserver_pid = -1; 23 | int strace_pid = -1; 24 | 25 | sigset_t old_mask; 26 | 27 | int init_socket() 28 | { 29 | struct sockaddr_in server_addr4; 30 | struct sockaddr_in6 server_addr6; 31 | int nOptval; 32 | 33 | // Command socket 34 | if (arg_opt_6) 35 | { 36 | CHECK((command_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) != -1); 37 | } 38 | else 39 | { 40 | CHECK((command_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1); 41 | } 42 | 43 | // Don't wait WAIT signal. 44 | nOptval = 1; 45 | CHECK(setsockopt(command_socket, SOL_SOCKET, SO_REUSEADDR, &nOptval, sizeof(int)) != -1); 46 | 47 | // Configure server address 48 | if (arg_opt_6) 49 | { 50 | memset(&server_addr6, 0, sizeof(server_addr6)); 51 | server_addr6.sin6_family = AF_INET6; 52 | inet_pton(AF_INET6, "::", &server_addr6.sin6_addr); 53 | server_addr6.sin6_port = htons(COMMAND_PORT); 54 | } 55 | else 56 | { 57 | memset(&server_addr4, 0, sizeof(server_addr4)); 58 | server_addr4.sin_family = AF_INET; 59 | inet_pton(AF_INET, "0.0.0.0", &server_addr4.sin_addr); 60 | server_addr4.sin_port = htons(COMMAND_PORT); 61 | } 62 | 63 | 64 | // Bind the socket to the server address 65 | if (arg_opt_6) 66 | { 67 | CHECK(bind(command_socket, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) != -1); 68 | } 69 | else 70 | { 71 | CHECK(bind(command_socket, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) != -1); 72 | } 73 | 74 | if(arg_opt_e) 75 | { 76 | // Service socket 77 | if (arg_opt_6) 78 | { 79 | CHECK((service_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) != -1); 80 | } 81 | else 82 | { 83 | CHECK((service_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1); 84 | } 85 | 86 | // Don't wait WAIT signal. 87 | nOptval = 1; 88 | CHECK(setsockopt(service_socket, SOL_SOCKET, SO_REUSEADDR, &nOptval, sizeof(int)) != -1); 89 | 90 | // Configure server address 91 | if (arg_opt_6) 92 | { 93 | memset(&server_addr6, 0, sizeof(server_addr6)); 94 | server_addr6.sin6_family = AF_INET6; 95 | inet_pton(AF_INET6, "::", &server_addr6.sin6_addr); 96 | server_addr6.sin6_port = htons(SERVICE_PORT); 97 | } 98 | else 99 | { 100 | memset(&server_addr4, 0, sizeof(server_addr4)); 101 | server_addr4.sin_family = AF_INET; 102 | inet_pton(AF_INET, "0.0.0.0", &server_addr4.sin_addr); 103 | server_addr4.sin_port = htons(SERVICE_PORT); 104 | } 105 | 106 | 107 | // Bind the socket to the server address 108 | if (arg_opt_6) 109 | { 110 | CHECK(bind(service_socket, (struct sockaddr *)&server_addr6, sizeof(server_addr6)) != -1); 111 | } 112 | else 113 | { 114 | CHECK(bind(service_socket, (struct sockaddr *)&server_addr4, sizeof(server_addr4)) != -1); 115 | } 116 | 117 | CHECK(listen(service_socket, 64) != -1); 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | int set_sig_handler() 124 | { 125 | sigset_t new_mask; 126 | 127 | sigemptyset(&new_mask); 128 | sigaddset(&new_mask, SIGINT); 129 | sigaddset(&new_mask, SIGCHLD); 130 | 131 | CHECK(sigprocmask(SIG_BLOCK, &new_mask, &old_mask) != -1); 132 | 133 | CHECK((signal_fd = signalfd(-1, &new_mask, 0)) != -1); 134 | 135 | return 0; 136 | } 137 | 138 | int monitor_fd(int fd) 139 | { 140 | struct epoll_event event; 141 | 142 | event.events = EPOLLIN; 143 | event.data.fd = fd; 144 | 145 | CHECK(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) != -1); 146 | 147 | return 0; 148 | } 149 | 150 | int init_fd() 151 | { 152 | CHECK(pipe(gdbserver_pipe) != -1); 153 | 154 | CHECK(pipe(strace_pipe) != -1); 155 | 156 | CHECK((epoll_fd = epoll_create(6)) != -1); 157 | 158 | init_socket(); 159 | 160 | set_sig_handler(); 161 | 162 | monitor_fd(command_socket); 163 | if(arg_opt_e) 164 | { 165 | monitor_fd(service_socket); 166 | } 167 | monitor_fd(signal_fd); 168 | monitor_fd(gdbserver_pipe[0]); 169 | monitor_fd(strace_pipe[0]); 170 | 171 | return 0; 172 | } -------------------------------------------------------------------------------- /src/sig.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "debug-server.h" 9 | 10 | char *sig_name[] = { 11 | "0", 12 | "SIGHUP", 13 | "SIGINT", 14 | "SIGQUIT", 15 | "SIGILL", 16 | "SIGTRAP", 17 | "SIGABRT", 18 | "7", 19 | "SIGFPE", 20 | "SIGKILL", 21 | "SIGBUS", 22 | "SIGSEGV", 23 | "SIGSYS", 24 | "SIGPIPE", 25 | "SIGALRM", 26 | "SIGTERM", 27 | "SIGURG", 28 | "SIGSTOP", 29 | "SIGTSTP", 30 | "SIGCONT", 31 | "SIGCHLD", 32 | "SIGTTIN", 33 | "SIGTTOU", 34 | "SIGPOLL", 35 | "SIGXCPU", 36 | "SIGXFSZ", 37 | "SIGVTALRM", 38 | "SIGPROF", 39 | "SIGWINCH", 40 | "29", 41 | "SIGUSR1", 42 | "SIGUSR2", 43 | "__SIGRTMIN" 44 | }; 45 | 46 | int child_signal_handler() 47 | { 48 | int pid = 0, status = 0; 49 | int i, is_con = 0, index; 50 | time_t spend; 51 | char signal_buf[0x100]; 52 | 53 | while(1) 54 | { 55 | pid = waitpid(-1, &status, WNOHANG); 56 | if(pid == 0 || pid == -1) 57 | { 58 | break; 59 | } 60 | 61 | if (WIFEXITED(status)) 62 | { 63 | if (pid == service_pid) 64 | { 65 | info_printf("Service exited, pid=%d, status=%d\n", pid, WEXITSTATUS(status)); 66 | service_pid = -1; 67 | } 68 | else if (pid == gdbserver_pid) 69 | { 70 | info_printf("Gdbserver exited, pid=%d, status=%d\n", pid, WEXITSTATUS(status)); 71 | gdbserver_pid = -1; 72 | } 73 | else if (pid == strace_pid) 74 | { 75 | info_printf("Strace exited, pid=%d, status=%d\n", pid, WEXITSTATUS(status)); 76 | strace_pid = -1; 77 | } 78 | else 79 | { 80 | info_printf("Unknown child process exited, pid=%d, status=%d\n", pid, WEXITSTATUS(status)); 81 | } 82 | } 83 | else if (WIFSIGNALED(status)) 84 | { 85 | memset(signal_buf, 0, sizeof(signal_buf)); 86 | if(WTERMSIG(status) < sizeof(sig_name)/sizeof(char*)) 87 | { 88 | strncpy(signal_buf, sig_name[WTERMSIG(status)], sizeof(signal_buf)-1); 89 | } 90 | else 91 | { 92 | snprintf(signal_buf, sizeof(signal_buf)-1, "%d", WTERMSIG(status)); 93 | } 94 | 95 | if (pid == service_pid) 96 | { 97 | info_printf("Service killed, pid=%d, signal=%s\n", pid, signal_buf); 98 | service_pid = -1; 99 | } 100 | else if (pid == gdbserver_pid) 101 | { 102 | info_printf("Gdbserver killed, pid=%d, signal=%s\n", pid, signal_buf); 103 | gdbserver_pid = -1; 104 | } 105 | else if (pid == strace_pid) 106 | { 107 | info_printf("Strace killed, pid=%d, signal=%s\n", pid, signal_buf); 108 | strace_pid = -1; 109 | } 110 | else 111 | { 112 | info_printf("Unknown child process killed, pid=%d, signal=%s\n", pid, signal_buf); 113 | } 114 | } 115 | else if (WIFSTOPPED(status)) 116 | { 117 | if(WTERMSIG(status) < sizeof(sig_name)/sizeof(char*)) 118 | { 119 | info_printf("Pid %d stopped by signal %s\n", pid, sig_name[WSTOPSIG(status)]); 120 | } 121 | else 122 | { 123 | info_printf("Pid %d stopped by signal %d\n", pid, WSTOPSIG(status)); 124 | } 125 | } 126 | else if (WIFCONTINUED(status)) 127 | { 128 | info_printf("Pid %d continued\n", pid); 129 | } 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | int signal_handler() 136 | { 137 | pid_t pid; 138 | struct signalfd_siginfo fdsi; 139 | int result; 140 | int ret_val = 1; 141 | int wstatus; 142 | 143 | CHECK((result = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo))) != -1); 144 | if(result == sizeof(struct signalfd_siginfo)) 145 | { 146 | switch (fdsi.ssi_signo) 147 | { 148 | case SIGINT: 149 | ret_val = 0; 150 | info_printf("Receive signal SIGINT\n"); 151 | break; 152 | 153 | case SIGQUIT: 154 | ret_val = 0; 155 | info_printf("Receive signal SIGQUIT\n"); 156 | break; 157 | 158 | case SIGTERM: 159 | ret_val = 0; 160 | info_printf("Receive signal SIGTERM\n"); 161 | break; 162 | 163 | case SIGCHLD: 164 | ret_val = 1; 165 | child_signal_handler(); 166 | break; 167 | 168 | default: 169 | warning_printf("Read unexpected signal %d\n", fdsi.ssi_signo); 170 | break; 171 | } 172 | } 173 | 174 | return ret_val; 175 | } -------------------------------------------------------------------------------- /src/tracer.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "debug-server.h" 10 | 11 | char *gdbserver_args[] = {"gdbserver", "--attach", /* Reserved parameter */ NULL, NULL, NULL}; 12 | char *strace_args[] = {"strace", "-f", "-p", /* Reserved parameter */ NULL, NULL}; 13 | 14 | int gdbserver_attach_pid(int pid) 15 | { 16 | char arg1[0x100], arg2[0x100]; 17 | char buf[0x200]; 18 | int run = 0; 19 | int result = 0; 20 | 21 | memset(arg1, 0, sizeof(arg1)); 22 | memset(arg2, 0, sizeof(arg2)); 23 | if (arg_opt_6) 24 | { 25 | snprintf(arg1, sizeof(arg1)-1, ":::%d", GDBSERVER_PORT); 26 | } 27 | else 28 | { 29 | snprintf(arg1, sizeof(arg1)-1, "0.0.0.0:%d", GDBSERVER_PORT); 30 | } 31 | snprintf(arg2, sizeof(arg2)-1, "%d", pid); 32 | gdbserver_args[sizeof(gdbserver_args)/sizeof(gdbserver_args[0])-3] = arg1; 33 | gdbserver_args[sizeof(gdbserver_args)/sizeof(gdbserver_args[0])-2] = arg2; 34 | 35 | if(gdbserver_pid != -1) 36 | { 37 | kill(gdbserver_pid, SIGTERM); 38 | CHECK(waitpid(gdbserver_pid, NULL, 0) == gdbserver_pid); 39 | gdbserver_pid = -1; 40 | } 41 | if(strace_pid != -1) 42 | { 43 | kill(strace_pid, SIGTERM); 44 | CHECK(waitpid(strace_pid, NULL, 0) == strace_pid); 45 | strace_pid = -1; 46 | } 47 | 48 | CHECK((gdbserver_pid = fork()) != -1); 49 | 50 | if(gdbserver_pid == 0) 51 | { 52 | CHECK(sigprocmask(SIG_SETMASK, &old_mask, NULL) != -1); 53 | 54 | CHECK(prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != -1); 55 | 56 | CHECK(dup2(gdbserver_pipe[1], STDERR_FILENO) != -1); 57 | 58 | close_fd(); 59 | 60 | if (execvp(gdbserver_args[0], gdbserver_args) == -1) 61 | { 62 | if (errno == ENOENT) 63 | { 64 | fprintf(stderr, "ERROR: \"%s\" cannot be found in PATH!\n", gdbserver_args[0]); 65 | } 66 | else if (errno == EACCES) 67 | { 68 | fprintf(stderr, "ERROR: \"%s\" does not have execution privileges!\n", gdbserver_args[0]); 69 | } 70 | else 71 | { 72 | fprintf(stderr, "ERROR: %s %s:%d\n", strerror(errno), __FILE__, __LINE__); 73 | } 74 | } 75 | 76 | exit(EXIT_FAILURE); 77 | } 78 | 79 | info_printf("Gdbserver start, pid=%d\n", gdbserver_pid); 80 | 81 | // Wait for gdbserver 82 | run = 1; 83 | while(run) 84 | { 85 | memset(buf, 0, sizeof(buf)); 86 | CHECK(read(gdbserver_pipe[0], buf, sizeof(buf)-1) >= 0); 87 | if(strstr(buf, "Listening on port")) 88 | { 89 | result = 1; 90 | run = 0; 91 | } 92 | else if(strstr(buf, "Exiting")) 93 | { 94 | result = 0; 95 | run = 0; 96 | } 97 | else if(strstr(buf, "info or a printed manual")) 98 | { 99 | result = 0; 100 | run = 0; 101 | } 102 | else if (strstr(buf, "ERROR")) 103 | { 104 | result = 0; 105 | run = 0; 106 | } 107 | gdbserver_output(buf); 108 | } 109 | 110 | if(arg_opt_s && stopped) 111 | { 112 | CHECK(kill(pid, SIGCONT) != -1); 113 | stopped = 0; 114 | } 115 | 116 | return result; 117 | } 118 | 119 | int strace_attach_pid(int pid) 120 | { 121 | char arg1[0x100]; 122 | char buf[0x100]; 123 | 124 | memset(arg1, 0, sizeof(arg1)); 125 | snprintf(arg1, sizeof(arg1)-1, "%d", pid); 126 | strace_args[sizeof(strace_args)/sizeof(strace_args[0])-2] = arg1; 127 | 128 | if(gdbserver_pid != -1) 129 | { 130 | kill(gdbserver_pid, SIGTERM); 131 | waitpid(gdbserver_pid, NULL, 0); 132 | gdbserver_pid = -1; 133 | } 134 | if(strace_pid != -1) 135 | { 136 | kill(strace_pid, SIGTERM); 137 | waitpid(strace_pid, NULL, 0); 138 | strace_pid = -1; 139 | } 140 | 141 | CHECK((strace_pid = fork()) != -1); 142 | 143 | if(strace_pid == 0) 144 | { 145 | CHECK(sigprocmask(SIG_SETMASK, &old_mask, NULL) != -1); 146 | 147 | CHECK(prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != -1); 148 | 149 | CHECK(dup2(strace_pipe[1], STDERR_FILENO) != -1); 150 | 151 | close_fd(); 152 | 153 | if (execvp(strace_args[0], strace_args) == -1) 154 | { 155 | if (errno == ENOENT) 156 | { 157 | fprintf(stderr, "ERROR: \"%s\" cannot be found in PATH!\n", strace_args[0]); 158 | } 159 | else if (errno == EACCES) 160 | { 161 | fprintf(stderr, "ERROR: \"%s\" does not have execution privileges!\n", strace_args[0]); 162 | } 163 | else 164 | { 165 | fprintf(stderr, "ERROR: %s %s:%d\n", strerror(errno), __FILE__, __LINE__); 166 | } 167 | } 168 | 169 | exit(EXIT_FAILURE); 170 | } 171 | 172 | info_printf("Strace start, pid=%d\n", strace_pid); 173 | 174 | memset(buf, 0, sizeof(buf)); 175 | CHECK(read(strace_pipe[0], buf, sizeof(buf)-1) > 0); 176 | strace_output(buf); 177 | 178 | if(arg_opt_s && stopped) 179 | { 180 | CHECK(kill(pid, SIGCONT) != -1); 181 | stopped = 0; 182 | } 183 | 184 | return 1; 185 | } -------------------------------------------------------------------------------- /src/service.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "debug-server.h" 13 | 14 | int stopped = 0; 15 | 16 | int close_fd() 17 | { 18 | if(epoll_fd != -1) 19 | { 20 | CHECK(close(epoll_fd) != -1); 21 | } 22 | if(command_socket != -1) 23 | { 24 | CHECK(close(command_socket) != -1); 25 | } 26 | if(service_socket != -1) 27 | { 28 | CHECK(close(service_socket) != -1); 29 | } 30 | if(signal_fd != -1) 31 | { 32 | CHECK(close(signal_fd) != -1); 33 | } 34 | if(gdbserver_pipe[0] != -1) 35 | { 36 | CHECK(close(gdbserver_pipe[0]) != -1); 37 | } 38 | if(gdbserver_pipe[1] != -1) 39 | { 40 | CHECK(close(gdbserver_pipe[1]) != -1); 41 | } 42 | if(strace_pipe[0] != -1) 43 | { 44 | CHECK(close(strace_pipe[0]) != -1); 45 | } 46 | if(strace_pipe[1] != -1) 47 | { 48 | CHECK(close(strace_pipe[1]) != -1); 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | int ptrace_for_stopping_at_entry_point(pid_t pid) 55 | { 56 | struct signalfd_siginfo fdsi; 57 | int result; 58 | int wstatus; 59 | pid_t tmp_pid; 60 | 61 | CHECK(ptrace(PTRACE_ATTACH, pid, NULL, 0, 0, 0) != -1); 62 | 63 | for(tmp_pid = -1; tmp_pid != pid;) 64 | { 65 | if(tmp_pid == -1) 66 | { 67 | CHECK((result = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo))) != -1); 68 | CHECK(fdsi.ssi_signo == SIGCHLD); 69 | } 70 | tmp_pid = waitpid(pid, &wstatus, 0); 71 | } 72 | CHECK((wstatus >> 16) == 0); 73 | debug_printf("Tracee(%d) is ready.\n", pid); 74 | 75 | CHECK(ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXEC|PTRACE_O_EXITKILL, 0, 0) != -1); 76 | CHECK(ptrace(PTRACE_CONT, pid, NULL, 0, 0, 0) != -1); 77 | CHECK((result = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo))) != -1); 78 | CHECK(fdsi.ssi_signo == SIGCHLD); 79 | CHECK(fdsi.ssi_pid == pid); 80 | CHECK(waitpid(pid, &wstatus, 0) == pid); 81 | CHECK((wstatus >> 16) == 0); 82 | debug_printf("Continue %d.\n", pid); 83 | 84 | CHECK(ptrace(PTRACE_CONT, pid, NULL, 0, 0, 0) != -1); 85 | CHECK((result = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo))) != -1); 86 | CHECK(fdsi.ssi_signo == SIGCHLD); 87 | CHECK(fdsi.ssi_pid == pid); 88 | CHECK(waitpid(pid, &wstatus, 0) == pid); 89 | CHECK(WIFSTOPPED(wstatus)); 90 | CHECK(WSTOPSIG(wstatus) == SIGTRAP); 91 | CHECK((wstatus >> 16) == PTRACE_EVENT_EXEC); 92 | debug_printf("Receive a PTRACE_EVENT_EXEC event from %d.\n", pid); 93 | 94 | CHECK(kill(pid, SIGSTOP) != -1); 95 | CHECK(ptrace(PTRACE_DETACH, pid, NULL, 0, 0, 0) != -1); 96 | CHECK((result = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo))) != -1); 97 | CHECK(fdsi.ssi_signo == SIGCHLD); 98 | CHECK(fdsi.ssi_pid == pid); 99 | 100 | return 0; 101 | } 102 | 103 | int start_service(int client_sock) 104 | { 105 | struct rlimit limit; 106 | 107 | if(gdbserver_pid != -1) 108 | { 109 | kill(gdbserver_pid, SIGTERM); 110 | CHECK(waitpid(gdbserver_pid, NULL, 0) == gdbserver_pid); 111 | gdbserver_pid = -1; 112 | } 113 | 114 | if(strace_pid != -1) 115 | { 116 | kill(strace_pid, SIGTERM); 117 | CHECK(waitpid(strace_pid, NULL, 0) == strace_pid); 118 | strace_pid = -1; 119 | } 120 | 121 | if(!arg_opt_m && service_pid != -1) 122 | { 123 | kill(service_pid, SIGKILL); 124 | CHECK(waitpid(service_pid, NULL, 0) == service_pid); 125 | service_pid = -1; 126 | } 127 | 128 | service_pid = fork(); 129 | if(service_pid == -1) 130 | { 131 | warning_printf("fork error %s:%d\n", __FILE__, __LINE__); 132 | } 133 | else if(service_pid == 0) 134 | { 135 | if(arg_opt_u == 0) 136 | { 137 | limit.rlim_cur = 0x100000000; 138 | limit.rlim_max = 0x100000000; 139 | CHECK(setrlimit(RLIMIT_AS, &limit) != -1); 140 | } 141 | 142 | CHECK(sigprocmask(SIG_SETMASK, &old_mask, NULL) != -1); 143 | 144 | CHECK(prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != -1); 145 | 146 | if(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == -1) 147 | { 148 | warning_printf("prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) failed\n"); 149 | } 150 | 151 | if(client_sock > 0) 152 | { 153 | CHECK(dup2(client_sock, STDIN_FILENO) != -1); 154 | CHECK(close(client_sock) != -1); 155 | } 156 | 157 | close_fd(); 158 | 159 | CHECK(setsid() != -1); 160 | 161 | if(arg_opt_n) 162 | { 163 | CHECK(personality(ADDR_NO_RANDOMIZE) != -1); 164 | } 165 | 166 | if(arg_opt_s) 167 | { 168 | CHECK(kill(0, SIGSTOP) != -1); 169 | } 170 | 171 | if(client_sock > 0) 172 | { 173 | dup2(STDIN_FILENO, STDOUT_FILENO); 174 | dup2(STDIN_FILENO, STDERR_FILENO); 175 | } 176 | 177 | if (execvp(arg_execve_argv[0], arg_execve_argv) == -1) 178 | { 179 | if (errno == ENOENT) 180 | { 181 | if (strstr(arg_execve_argv[0], "/")) 182 | { 183 | fprintf(stderr, "ERROR: \"%s\" does not exist!\n", arg_execve_argv[0]); 184 | } 185 | else 186 | { 187 | fprintf(stderr, "ERROR: \"%s\" cannot be found in PATH. Perhaps try running \"./%s\"!\n", arg_execve_argv[0], arg_execve_argv[0]); 188 | } 189 | } 190 | else if (errno == EACCES) 191 | { 192 | fprintf(stderr, "ERROR: \"%s\" does not have execution privileges!\n", arg_execve_argv[0]); 193 | } 194 | else 195 | { 196 | fprintf(stderr, "ERROR: %s %s:%d\n", strerror(errno), __FILE__, __LINE__); 197 | } 198 | } 199 | exit(EXIT_FAILURE); 200 | } 201 | else 202 | { 203 | if(arg_opt_s) 204 | { 205 | ptrace_for_stopping_at_entry_point(service_pid); 206 | stopped = 1; 207 | } 208 | 209 | info_printf("Service start, pid=%d\n", service_pid); 210 | } 211 | 212 | return 0; 213 | } 214 | 215 | int service_handler() 216 | { 217 | int client_sock = -1; 218 | socklen_t client_addr_size; 219 | struct sockaddr_in client_addr4; 220 | struct sockaddr_in6 client_addr6; 221 | char clientIP[INET6_ADDRSTRLEN], ip_buf[0x100]; 222 | int clientPort; 223 | 224 | if (arg_opt_6) 225 | { 226 | client_addr_size = sizeof(client_addr6); 227 | CHECK((client_sock = accept(service_socket, (struct sockaddr *)&client_addr6, &client_addr_size)) != -1); 228 | } 229 | else 230 | { 231 | client_addr_size = sizeof(client_addr4); 232 | CHECK((client_sock = accept(service_socket, (struct sockaddr *)&client_addr4, &client_addr_size)) != -1); 233 | } 234 | 235 | // Get the client's IP address and port 236 | memset(clientIP, 0, sizeof(clientIP)); 237 | if (arg_opt_6) 238 | { 239 | inet_ntop(AF_INET6, &(client_addr6.sin6_addr), clientIP, INET6_ADDRSTRLEN); 240 | } 241 | else 242 | { 243 | inet_ntop(AF_INET, &(client_addr4.sin_addr), clientIP, INET_ADDRSTRLEN); 244 | } 245 | memset(ip_buf, 0, sizeof(ip_buf)); 246 | if (arg_opt_6) 247 | { 248 | if (clientIP[0] == ':') 249 | { 250 | snprintf(ip_buf, sizeof(ip_buf)-1, "%s", clientIP + 7); 251 | } 252 | else 253 | { 254 | snprintf(ip_buf, sizeof(ip_buf)-1, "[%s]", clientIP); 255 | } 256 | } 257 | else 258 | { 259 | snprintf(ip_buf, sizeof(ip_buf)-1, "%s", clientIP); 260 | } 261 | 262 | if (arg_opt_6) 263 | { 264 | clientPort = ntohs(client_addr6.sin6_port); 265 | } 266 | else 267 | { 268 | clientPort = ntohs(client_addr4.sin_port); 269 | } 270 | 271 | debug_printf("Receive %s:%d from service_sock\n", ip_buf, clientPort); 272 | 273 | start_service(client_sock); 274 | 275 | if(client_sock != -1) 276 | { 277 | CHECK(close(client_sock) != -1); 278 | } 279 | 280 | return 1; 281 | } -------------------------------------------------------------------------------- /src/command.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "debug-server.h" 7 | 8 | int popen_to_int(char *cmd) 9 | { 10 | FILE *fp = NULL; 11 | char buf[0x100]; 12 | int result; 13 | 14 | CHECK((fp = popen(cmd, "r")) != NULL); 15 | 16 | memset(buf, 0, sizeof(buf)); 17 | CHECK((result = fread(buf, sizeof(buf[0]), sizeof(buf)-1, fp)) >= 0); 18 | 19 | CHECK(pclose(fp) != -1); 20 | 21 | return atoi(buf); 22 | } 23 | 24 | size_t get_address(int pid, char *search) 25 | { 26 | char buf[0x100]; 27 | char buf2[0x1000]; 28 | int fd; 29 | size_t result = 0; 30 | int i; 31 | char chr; 32 | int eof = 0; 33 | 34 | memset(buf, 0, sizeof(buf)); 35 | snprintf(buf, sizeof(buf)-1, "/proc/%d/maps", pid); 36 | fd = open(buf, O_RDONLY); 37 | if(fd != -1) 38 | { 39 | for(eof = 0; eof != 1;) 40 | { 41 | memset(buf2, 0, sizeof(buf2)); 42 | for(i = 0; eof != 1 && i < sizeof(buf2) - 1; i++) 43 | { 44 | if(read(fd, &chr, sizeof(chr)) != 1) 45 | { 46 | eof = 1; 47 | } 48 | 49 | buf2[i] = chr; 50 | 51 | if(chr == '\n' || chr == '\0') 52 | { 53 | buf2[i] = '\0'; 54 | break; 55 | } 56 | } 57 | 58 | if(strstr(buf2, search)) 59 | { 60 | for(i = 0; i < sizeof(buf2) && buf2[i] && buf2[i] != '-'; i++) 61 | { 62 | if(buf2[i] >= '0' && buf2[i] <= '9') 63 | { 64 | result = (result << 4) + (buf2[i] - '0'); 65 | } 66 | else if(buf2[i] >= 'a' && buf2[i] <= 'f') 67 | { 68 | result = (result << 4) + (buf2[i] - 'a' + 10); 69 | } 70 | } 71 | break; 72 | } 73 | } 74 | close(fd); 75 | } 76 | else 77 | { 78 | warning_printf("%s open failed.\n", buf); 79 | } 80 | return result; 81 | } 82 | 83 | int command_handler() 84 | { 85 | char buf[0x100]; 86 | socklen_t client_addr_size; 87 | struct sockaddr_in client_addr4; 88 | struct sockaddr_in6 client_addr6; 89 | int recv_len; 90 | unsigned char command, path_len; 91 | size_t addr = 0; 92 | char clientIP[INET6_ADDRSTRLEN], ip_buf[0x100]; 93 | int clientPort; 94 | int pid; 95 | int gdb_attached = 0; 96 | 97 | if (arg_opt_6) 98 | { 99 | client_addr_size = sizeof(client_addr6); 100 | memset(buf, 0, sizeof(buf)); 101 | recv_len = recvfrom(command_socket, buf, sizeof(buf)-1, 0, (struct sockaddr *)&client_addr6, &client_addr_size); 102 | } 103 | else 104 | { 105 | client_addr_size = sizeof(client_addr4); 106 | memset(buf, 0, sizeof(buf)); 107 | recv_len = recvfrom(command_socket, buf, sizeof(buf)-1, 0, (struct sockaddr *)&client_addr4, &client_addr_size); 108 | } 109 | 110 | if (arg_opt_6) 111 | { 112 | memset(clientIP, 0, sizeof(clientIP)); 113 | inet_ntop(AF_INET6, &(client_addr6.sin6_addr), clientIP, INET6_ADDRSTRLEN); 114 | memset(ip_buf, 0, sizeof(ip_buf)); 115 | if (clientIP[0] == ':') 116 | { 117 | snprintf(ip_buf, sizeof(ip_buf)-1, "%s", clientIP + 7); 118 | } 119 | else 120 | { 121 | snprintf(ip_buf, sizeof(ip_buf)-1, "[%s]", clientIP); 122 | } 123 | clientPort = ntohs(client_addr6.sin6_port); 124 | } 125 | else 126 | { 127 | memset(clientIP, 0, sizeof(clientIP)); 128 | inet_ntop(AF_INET, &(client_addr4.sin_addr), clientIP, INET_ADDRSTRLEN); 129 | memset(ip_buf, 0, sizeof(ip_buf)); 130 | snprintf(ip_buf, sizeof(ip_buf)-1, "%s", clientIP); 131 | clientPort = ntohs(client_addr4.sin_port); 132 | } 133 | 134 | 135 | command = buf[0]; 136 | switch(command) 137 | { 138 | case COMMAND_GDB_REGISTER: 139 | if (arg_opt_6) 140 | { 141 | memcpy(&gdb_client_address6, &client_addr6, sizeof(gdb_client_address6)); 142 | } 143 | else 144 | { 145 | memcpy(&gdb_client_address4, &client_addr4, sizeof(gdb_client_address4)); 146 | } 147 | info_printf("%s gdb client registered.\n", ip_buf); 148 | 149 | if (arg_opt_6) 150 | { 151 | client_addr_size = sizeof(client_addr6); 152 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr6, client_addr_size) != -1); 153 | } 154 | else 155 | { 156 | client_addr_size = sizeof(client_addr4); 157 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr4, client_addr_size) != -1); 158 | } 159 | 160 | break; 161 | case COMMAND_GDBSERVER_ATTACH: 162 | debug_printf("Receive %s:%d from command_sock to COMMAND_GDBSERVER_ATTACH\n", ip_buf, clientPort); 163 | if((arg_opt_6 && gdb_client_address6.sin6_family) || ((!arg_opt_6) && gdb_client_address4.sin_family)) 164 | { 165 | if(arg_opt_p) 166 | { 167 | pid = arg_pid; 168 | if(pid) 169 | { 170 | gdb_attached = gdbserver_attach_pid(pid); 171 | } 172 | else 173 | { 174 | warning_printf("There is an issue with the PID \"%s\".\n", arg_popen); 175 | } 176 | } 177 | else if(arg_opt_o) 178 | { 179 | pid = popen_to_int(arg_popen); 180 | if(pid) 181 | { 182 | gdb_attached = gdbserver_attach_pid(pid); 183 | } 184 | else 185 | { 186 | warning_printf("There is an issue with the CMD \"%s\".\n", arg_popen); 187 | } 188 | } 189 | else if(service_pid != -1) 190 | { 191 | gdb_attached = gdbserver_attach_pid(service_pid); 192 | } 193 | else 194 | { 195 | warning_printf("There are no PIDs available for tracing!\n"); 196 | } 197 | 198 | if(gdb_attached) 199 | { 200 | if (arg_opt_6) 201 | { 202 | client_addr_size = sizeof(gdb_client_address6); 203 | // Send the received data back to the two client 204 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&gdb_client_address6, client_addr_size) != -1); 205 | } 206 | else 207 | { 208 | client_addr_size = sizeof(gdb_client_address4); 209 | // Send the received data back to the two client 210 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&gdb_client_address4, client_addr_size) != -1); 211 | } 212 | } 213 | } 214 | else 215 | { 216 | warning_printf("There is no gdb client\n"); 217 | } 218 | 219 | if (arg_opt_6) 220 | { 221 | client_addr_size = sizeof(client_addr6); 222 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr6, client_addr_size) != -1); 223 | } 224 | else 225 | { 226 | client_addr_size = sizeof(client_addr4); 227 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr4, client_addr_size) != -1); 228 | } 229 | 230 | break; 231 | case COMMAND_STRACE_ATTACH: 232 | debug_printf("Receive %s:%d from command_sock to COMMAND_STRACE_ATTACH\n", ip_buf, clientPort); 233 | if(arg_opt_p) 234 | { 235 | pid = arg_pid; 236 | if(pid) 237 | { 238 | strace_attach_pid(pid); 239 | } 240 | else 241 | { 242 | warning_printf("There is an issue with the PID \"%s\".\n", arg_popen); 243 | } 244 | } 245 | if(arg_opt_o) 246 | { 247 | pid = popen_to_int(arg_popen); 248 | if(pid) 249 | { 250 | strace_attach_pid(pid); 251 | } 252 | else 253 | { 254 | warning_printf("There is an issue with the CMD \"%s\".\n", arg_popen); 255 | } 256 | } 257 | else if(service_pid != -1) 258 | { 259 | strace_attach_pid(service_pid); 260 | } 261 | else 262 | { 263 | warning_printf("There are no PIDs available for tracing!\n"); 264 | } 265 | 266 | if (arg_opt_6) 267 | { 268 | client_addr_size = sizeof(client_addr6); 269 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr6, client_addr_size) != -1); 270 | } 271 | else 272 | { 273 | client_addr_size = sizeof(client_addr4); 274 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr4, client_addr_size) != -1); 275 | } 276 | 277 | break; 278 | case COMMAND_GET_ADDRESS: 279 | debug_printf("Receive %s:%d from command_sock to COMMAND_GET_ADDRESS\n", ip_buf, clientPort); 280 | addr = 0; 281 | if(arg_opt_o) 282 | { 283 | addr = get_address(popen_to_int(arg_popen), buf + 2); 284 | } 285 | else if(service_pid != -1) 286 | { 287 | addr = get_address(service_pid, buf + 2); 288 | } 289 | 290 | memset(buf, 0, sizeof(buf)); 291 | buf[0] = COMMAND_GET_ADDRESS; 292 | buf[1] = sizeof(addr); 293 | *(size_t*)&buf[2] = addr; 294 | 295 | if (arg_opt_6) 296 | { 297 | client_addr_size = sizeof(client_addr6); 298 | CHECK(sendto(command_socket, buf, 2 + sizeof(addr), 0, (struct sockaddr *)&client_addr6, client_addr_size) != -1); 299 | } 300 | else 301 | { 302 | client_addr_size = sizeof(client_addr4); 303 | CHECK(sendto(command_socket, buf, 2 + sizeof(addr), 0, (struct sockaddr *)&client_addr4, client_addr_size) != -1); 304 | } 305 | 306 | 307 | break; 308 | case COMMAND_GDB_LOGOUT: 309 | warning_printf("Receive COMMAND_GDB_LOGOUT from %s:%d\n", ip_buf, clientPort); 310 | break; 311 | case COMMAND_RUN_SERVICE: 312 | debug_printf("Receive %s:%d from command_sock to COMMAND_RUN_SERVICE\n", ip_buf, clientPort); 313 | start_service(0); 314 | 315 | if (arg_opt_6) 316 | { 317 | client_addr_size = sizeof(client_addr6); 318 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr6, client_addr_size) != -1); 319 | } 320 | else 321 | { 322 | client_addr_size = sizeof(client_addr4); 323 | CHECK(sendto(command_socket, buf, recv_len, 0, (struct sockaddr *)&client_addr4, client_addr_size) != -1); 324 | } 325 | 326 | break; 327 | default: 328 | warning_printf("Unknown command 0x%02X\n", command); 329 | break; 330 | } 331 | 332 | return 1; 333 | } 334 | 335 | int disconnect_gdb() 336 | { 337 | socklen_t client_addr_size; 338 | char buf[0x10]; 339 | 340 | if (arg_opt_6) 341 | { 342 | if(gdb_client_address6.sin6_family) 343 | { 344 | client_addr_size = sizeof(gdb_client_address6); 345 | memset(buf, 0, sizeof(buf)); 346 | buf[0] = COMMAND_GDB_LOGOUT; 347 | CHECK(sendto(command_socket, buf, 1, 0, (struct sockaddr *)&gdb_client_address6, client_addr_size) != -1); 348 | } 349 | } 350 | else 351 | { 352 | if(gdb_client_address4.sin_family) 353 | { 354 | client_addr_size = sizeof(gdb_client_address4); 355 | memset(buf, 0, sizeof(buf)); 356 | buf[0] = COMMAND_GDB_LOGOUT; 357 | CHECK(sendto(command_socket, buf, 1, 0, (struct sockaddr *)&gdb_client_address4, client_addr_size) != -1); 358 | } 359 | } 360 | 361 | return 0; 362 | } --------------------------------------------------------------------------------