/* radare - LGPL - Copyright 2007-2013 - pancake */ #include #include #include #include /* only used for BSD PTRACE redefinitions */ #define r_io_redirect(io,file) \ free (io->redirect); \ io->redirect = file? strdup (file): NULL #if __linux__ || __APPLE__ || __WINDOWS__ || \ __NetBSD__ || __KFBSD__ || __OpenBSD__ #define DEBUGGER_SUPPORTED 1 #else #define DEBUGGER_SUPPORTED 0 #endif #if DEBUGGER && DEBUGGER_SUPPORTED #define MAGIC_EXIT 123 #include #if __UNIX__ #include #include #include #endif #if __APPLE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #if __APPLE__ || __BSD__ static void inferior_abort_handler(int pid) { eprintf ("Inferior received signal SIGABRT. Executing BKPT.\n"); } #endif /* * Creates a new process and returns the result: * -1 : error * 0 : ok */ #if __WINDOWS__ #include #include #include #include static int setup_tokens() { HANDLE tok; TOKEN_PRIVILEGES tp; DWORD err; tok = NULL; err = -1; if (!OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES, &tok)) goto err_enable; tp.PrivilegeCount = 1; if (!LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) goto err_enable; //tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; tp.Privileges[0].Attributes = 0; //SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges (tok, 0, &tp, sizeof (tp), NULL, NULL)) goto err_enable; err = 0; err_enable: if (tok != NULL) CloseHandle (tok); if (err) r_sys_perror ("setup_tokens"); return err; } static int fork_and_ptraceme(int bits, const char *cmd) { PROCESS_INFORMATION pi; STARTUPINFO si = { sizeof (si) }; DEBUG_EVENT de; int pid, tid; HANDLE th = INVALID_HANDLE_VALUE; if (!*cmd) return -1; setup_tokens (); /* TODO: with args */ if (!CreateProcess (cmd, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi)) { r_sys_perror ("CreateProcess"); return -1; } /* get process id and thread id */ pid = pi.dwProcessId; tid = pi.dwThreadId; #if 0 /* load thread list */ { HANDLE h; THREADENTRY32 te32; HANDLE WINAPI (*win32_openthread)(DWORD, BOOL, DWORD) = NULL; win32_openthread = (HANDLE WINAPI (*)(DWORD, BOOL, DWORD)) GetProcAddress (GetModuleHandle ("kernel32"), "OpenThread"); th = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, pid); if (th == INVALID_HANDLE_VALUE || !Thread32First(th, &te32)) r_sys_perror ("CreateToolhelp32Snapshot"); do { if (te32.th32OwnerProcessID == pid) { h = win32_openthread (THREAD_ALL_ACCESS, 0, te32.th32ThreadID); if (h == NULL) r_sys_perror ("OpenThread"); else eprintf ("HANDLE=%p\n", h); } } while (Thread32Next (th, &te32)); } #endif #if 0 // Access denied here :? if (ContinueDebugEvent (pid, tid, DBG_CONTINUE) == 0) { r_sys_perror ("ContinueDebugEvent"); goto err_fork; } #endif /* catch create process event */ if (!WaitForDebugEvent (&de, 10000)) goto err_fork; /* check if is a create process debug event */ if (de.dwDebugEventCode != CREATE_PROCESS_DEBUG_EVENT) { eprintf ("exception code 0x%04x\n", (ut32)de.dwDebugEventCode); goto err_fork; } if (th != INVALID_HANDLE_VALUE) CloseHandle (th); eprintf ("PID=%d\n", pid); eprintf ("TID=%d\n", tid); return pid; err_fork: eprintf ("ERRFORK\n"); TerminateProcess (pi.hProcess, 1); if (th != INVALID_HANDLE_VALUE) CloseHandle (th); return -1; } #else static int fork_and_ptraceme(int bits, const char *cmd) { char **argv; int ret, status, pid = fork (); switch (pid) { case -1: perror ("fork_and_ptraceme"); break; case 0: #if __APPLE__ signal (SIGTRAP, SIG_IGN); //NEED BY STEP #endif #if __APPLE__ || __BSD__ /* we can probably remove this #if..as long as PT_TRACE_ME is redefined for OSX in r_debug.h */ signal (SIGABRT, inferior_abort_handler); if (ptrace (PT_TRACE_ME, 0, 0, 0) != 0) { #else if (ptrace (PTRACE_TRACEME, 0, NULL, NULL) != 0) { #endif eprintf ("ptrace-traceme failed\n"); exit (MAGIC_EXIT); } // TODO: Add support to redirect filedescriptors // TODO: Configure process environment argv = r_str_argv (cmd, NULL); #if __APPLE__ #include { posix_spawnattr_t attr = {0}; size_t copied = 1; cpu_type_t cpu; int ret; pid_t p = -1; posix_spawnattr_init (&attr); posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETEXEC); #if __i386__ || __x86_64__ cpu = CPU_TYPE_I386; if (bits == 64) cpu |= CPU_ARCH_ABI64; #else cpu = CPU_TYPE_ANY; #endif posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &copied); ret = posix_spawnp (&p, argv[0], NULL, &attr, argv, NULL); switch (ret) { case 0: eprintf ("Success\n"); break; case 22: eprintf ("posix_spawnp: Invalid argument\n"); break; case 86: eprintf ("Unsupported architecture\n"); break; default: eprintf ("posix_spawnp: unknown error %d\n", ret); perror ("posix_spawnp"); break; } /* only required if no SETEXEC called if (p != -1) wait (p); */ exit (MAGIC_EXIT); /* error */ } #else execvp (argv[0], argv); #endif r_str_argv_free (argv); perror ("fork_and_attach: execv"); //printf(stderr, "[%d] %s execv failed.\n", getpid(), ps.filename); exit (MAGIC_EXIT); /* error */ return 0; // invalid pid // if exit is overriden.. :) default: /* XXX: clean this dirty code */ ret = wait (&status); if (ret != pid) eprintf ("Wait event received by different pid %d\n", ret); if (WIFSTOPPED (status)) eprintf ("Process with PID %d started...\n", (int)pid); if (WEXITSTATUS (status) == MAGIC_EXIT) pid = -1; // XXX kill (pid, SIGSTOP); break; } eprintf ("PID = %d\n", pid); return pid; } #endif static int __plugin_open(RIO *io, const char *file) { if (!memcmp (file, "dbg://", 6) && file[6]) return R_TRUE; return R_FALSE; } static RIODesc *__open(RIO *io, const char *file, int rw, int mode) { char uri[128]; if (__plugin_open (io, file)) { int pid = atoi (file+6); if (pid == 0) { pid = fork_and_ptraceme (io->bits, file+6); if (pid==-1) return NULL; #if __WINDOWS__ sprintf (uri, "w32dbg://%d", pid); #elif __APPLE__ sprintf (uri, "mach://%d", pid); #else // TODO: use io_procpid here? faster or what? sprintf (uri, "ptrace://%d", pid); #endif r_io_redirect (io, uri); } else { sprintf (uri, "attach://%d", pid); r_io_redirect (io, uri); } return NULL; } r_io_redirect (io, NULL); return NULL; } RIOPlugin r_io_plugin_debug = { //void *plugin; .name = "debug", .desc = "Debug a program or pid. dbg:///bin/ls, dbg://1388", .license = "LGPL3", .open = __open, .plugin_open = __plugin_open, .lseek = NULL, .system = NULL, .debug = (void *)(size_t)1, //void *widget; /* struct debug_t *debug; ut32 (*write)(int fd, const ut8 *buf, ut32 count); int fds[R_IO_NFDS]; */ }; #else // DEBUGGER struct r_io_plugin_t r_io_plugin_debug = { .name = "debug", .desc = "Debug a program or pid. (NOT SUPPORTED FOR THIS PLATFORM)", .debug = (void *)1, }; #endif // DEBUGGER #ifndef CORELIB struct r_lib_struct_t radare_plugin = { .type = R_LIB_TYPE_IO, .data = &r_io_plugin_debug }; #endif