forcezero.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. /*
  2. * forcezero — make a running process terminate with a chosen exit status.
  3. *
  4. * Usage: forcezero <pid> [exit-code] (exit-code defaults to 0, range 0..255)
  5. * forcezero -h | --help
  6. *
  7. * How it works (x86-64 Linux, PTRACE_SEIZE based):
  8. *
  9. * 1. Seize the target and interrupt it (no SIGSTOP is ever delivered).
  10. * 2. Read the target's ELF header through /proc/<pid>/maps + ptrace and
  11. * resolve the runtime address of its entry point (_start, which is dead
  12. * code once the process has started).
  13. * 3. Overwrite the first 12 bytes there with an exit_group() payload that is
  14. * embedded in this binary — nothing is ever written to disk:
  15. *
  16. * mov edi, rc ; mov eax, 231 ; syscall (64-bit target)
  17. * mov ebx, rc ; mov eax, 252 ; int 0x80 (32-bit compat target)
  18. *
  19. * 4. Point RIP at the payload and detach. Every thread of the target exits
  20. * with the requested status, which propagates to whoever waits on it
  21. * (shell, CI runner, build system, ...).
  22. *
  23. * The payload needs no libc, no symbols and no writable page inside the target,
  24. * so static, stripped and setuid binaries work alike (given the privileges to
  25. * trace them). Targets that are job-control stopped stay stopped; they exit
  26. * with the status once continued.
  27. *
  28. * Permissions: tracing needs the same uid (with yama ptrace_scope 0) or
  29. * root / CAP_SYS_PTRACE. Typical invocation: `sudo forcezero <pid>`.
  30. *
  31. * Build: cc -O2 -Wall -Wextra -o forcezero forcezero.c
  32. */
  33. #if !defined(__x86_64__)
  34. #error "forcezero supports x86-64 hosts only (x86-64 and i386 targets)"
  35. #endif
  36. #define _GNU_SOURCE
  37. #include <elf.h>
  38. #include <errno.h>
  39. #include <stdarg.h>
  40. #include <stdint.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #include <sys/ptrace.h>
  45. #include <sys/stat.h>
  46. #include <sys/sysmacros.h>
  47. #include <sys/types.h>
  48. #include <sys/user.h>
  49. #include <sys/wait.h>
  50. #include <unistd.h>
  51. #define MAP_MAX 4096
  52. #define PAYLOAD_LEN 12
  53. #define PATCH_WORDS 3
  54. struct mapping {
  55. uint64_t start, end, file_off;
  56. unsigned dev_major, dev_minor;
  57. unsigned long inode;
  58. int exec;
  59. };
  60. struct inject_state {
  61. int attached;
  62. int memory_patched;
  63. int regs_set;
  64. uint64_t win_addr;
  65. size_t win_words;
  66. long saved_words[PATCH_WORDS];
  67. struct user_regs_struct regs; /* original registers */
  68. struct mapping maps[MAP_MAX];
  69. size_t nmaps;
  70. };
  71. static const char *prog = "forcezero";
  72. static struct inject_state state;
  73. /* ---- diagnostics ---------------------------------------------------------- */
  74. static void perr(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
  75. static void perr(const char *fmt, ...)
  76. {
  77. int err = errno;
  78. va_list ap;
  79. fprintf(stderr, "%s: ", prog);
  80. va_start(ap, fmt);
  81. vfprintf(stderr, fmt, ap);
  82. va_end(ap);
  83. if (err)
  84. fprintf(stderr, ": %s", strerror(err));
  85. fputc('\n', stderr);
  86. }
  87. /* ---- target memory access ------------------------------------------------- */
  88. static int peek_word(pid_t pid, uint64_t addr, long *out)
  89. {
  90. long v;
  91. errno = 0;
  92. v = ptrace(PTRACE_PEEKDATA, pid, (void *)(uintptr_t)addr, NULL);
  93. if (v == -1 && errno)
  94. return -1;
  95. *out = v;
  96. return 0;
  97. }
  98. static int poke_word(pid_t pid, uint64_t addr, long v)
  99. {
  100. return ptrace(PTRACE_POKEDATA, pid, (void *)(uintptr_t)addr, (void *)(uintptr_t)v);
  101. }
  102. /*
  103. * Overwrite [addr, addr+len) in the target. Writes happen in whole words, so
  104. * the surrounding bytes of the first and last word are read first and merged
  105. * back; `saved` receives the original window for a later restore_memory().
  106. */
  107. static int patch_memory(pid_t pid, uint64_t addr, const unsigned char *bytes, size_t len,
  108. uint64_t *win_addr, size_t *win_words, long *saved)
  109. {
  110. uint64_t start = addr & ~(uint64_t)7;
  111. size_t off = (size_t)(addr - start);
  112. size_t words = (off + len + 7) / 8;
  113. unsigned char win[PATCH_WORDS * 8];
  114. size_t i;
  115. if (words > PATCH_WORDS) {
  116. errno = EINVAL;
  117. return -1;
  118. }
  119. for (i = 0; i < words; i++) {
  120. long w;
  121. if (peek_word(pid, start + i * 8, &w) != 0)
  122. return -1;
  123. saved[i] = w;
  124. memcpy(win + i * 8, &w, 8);
  125. }
  126. memcpy(win + off, bytes, len);
  127. for (i = 0; i < words; i++) {
  128. long w;
  129. memcpy(&w, win + i * 8, 8);
  130. if (poke_word(pid, start + i * 8, w) != 0)
  131. return -1;
  132. }
  133. *win_addr = start;
  134. *win_words = words;
  135. return 0;
  136. }
  137. static void restore_memory(pid_t pid, uint64_t win_addr, size_t win_words, const long *saved)
  138. {
  139. size_t i;
  140. for (i = 0; i < win_words; i++)
  141. (void)poke_word(pid, win_addr + i * 8, saved[i]);
  142. }
  143. /* ---- /proc/<pid>/maps ----------------------------------------------------- */
  144. static int read_maps(pid_t pid, struct mapping *maps, size_t max, size_t *n)
  145. {
  146. char path[64], line[1024];
  147. FILE *fp;
  148. snprintf(path, sizeof(path), "/proc/%ld/maps", (long)pid);
  149. fp = fopen(path, "r");
  150. if (!fp)
  151. return -1;
  152. *n = 0;
  153. while (fgets(line, sizeof(line), fp) && *n < max) {
  154. struct mapping m;
  155. char perms[8];
  156. unsigned long long start, end, off;
  157. unsigned maj, min;
  158. unsigned long ino;
  159. if (sscanf(line, "%llx-%llx %7s %llx %x:%x %lu",
  160. &start, &end, perms, &off, &maj, &min, &ino) != 7)
  161. continue;
  162. m.start = start;
  163. m.end = end;
  164. m.file_off = off;
  165. m.exec = (perms[2] == 'x');
  166. m.dev_major = maj;
  167. m.dev_minor = min;
  168. m.inode = ino;
  169. maps[(*n)++] = m;
  170. }
  171. fclose(fp);
  172. return 0;
  173. }
  174. static const struct mapping *map_containing(const struct mapping *maps, size_t n,
  175. uint64_t addr, int need_exec)
  176. {
  177. size_t i;
  178. for (i = 0; i < n; i++)
  179. if (addr >= maps[i].start && addr < maps[i].end && (!need_exec || maps[i].exec))
  180. return &maps[i];
  181. return NULL;
  182. }
  183. static int exe_devino(pid_t pid, unsigned *maj, unsigned *min, unsigned long *ino)
  184. {
  185. char path[64];
  186. struct stat st;
  187. snprintf(path, sizeof(path), "/proc/%ld/exe", (long)pid);
  188. if (stat(path, &st) != 0)
  189. return -1;
  190. *maj = major(st.st_dev);
  191. *min = minor(st.st_dev);
  192. *ino = st.st_ino;
  193. return 0;
  194. }
  195. /*
  196. * Find where to put the payload. Preferred spot is the entry point of the
  197. * main executable: it is guaranteed to be executable, page-aligned enough to
  198. * hold 12 bytes, and dead code for any process that already started. If the
  199. * ELF header is unavailable, fall back to the first roomy executable mapping.
  200. */
  201. static int find_patch_addr(pid_t pid, const struct mapping *maps, size_t n,
  202. int is64, uint64_t *out)
  203. {
  204. unsigned maj = 0, min = 0;
  205. unsigned long ino = 0;
  206. uint64_t base = 0;
  207. int have_exe = 0, have_base = 0;
  208. size_t i;
  209. *out = 0;
  210. if (exe_devino(pid, &maj, &min, &ino) == 0) {
  211. have_exe = 1;
  212. for (i = 0; i < n; i++) {
  213. if (maps[i].inode == ino && maps[i].dev_major == maj &&
  214. maps[i].dev_minor == min && maps[i].file_off == 0) {
  215. base = maps[i].start;
  216. have_base = 1;
  217. break;
  218. }
  219. }
  220. }
  221. if (have_base) {
  222. long w0, wtype, wentry;
  223. if (peek_word(pid, base, &w0) == 0 && peek_word(pid, base + 16, &wtype) == 0 &&
  224. peek_word(pid, base + 24, &wentry) == 0 && (w0 & 0xffffffffU) == 0x464c457fU) {
  225. int etype = (int)((unsigned long)wtype & 0xffffU);
  226. uint64_t entry = is64 ? (uint64_t)wentry : (uint32_t)wentry;
  227. if (etype == ET_DYN)
  228. entry += base;
  229. if (etype == ET_DYN || etype == ET_EXEC) {
  230. const struct mapping *m = map_containing(maps, n, entry, 1);
  231. if (m && PAYLOAD_LEN <= m->end - entry) {
  232. *out = entry;
  233. return 0;
  234. }
  235. }
  236. }
  237. }
  238. /* Fallback: a roomy executable page of the main executable that is not
  239. * currently running any live function of interest. */
  240. for (i = 0; i < n; i++) {
  241. uint64_t a = maps[i].start + 0x100;
  242. if (!maps[i].exec)
  243. continue;
  244. if (have_exe && !(maps[i].inode == ino && maps[i].dev_major == maj &&
  245. maps[i].dev_minor == min))
  246. continue;
  247. if (a + PAYLOAD_LEN <= maps[i].end) {
  248. *out = a;
  249. return 0;
  250. }
  251. }
  252. /* Last resort: any file-backed executable mapping. */
  253. for (i = 0; i < n; i++) {
  254. uint64_t a = maps[i].start + 0x100;
  255. if (maps[i].exec && maps[i].inode != 0 && a + PAYLOAD_LEN <= maps[i].end) {
  256. *out = a;
  257. return 0;
  258. }
  259. }
  260. return -1;
  261. }
  262. /* ---- payload -------------------------------------------------------------- */
  263. /*
  264. * The injectable payload, embedded verbatim in this binary. The first four
  265. * bytes of the "mov reg, imm32" instruction are patched with the requested
  266. * exit code at runtime; everything else is fixed.
  267. */
  268. static const unsigned char payload_tmpl[2][PAYLOAD_LEN] = {
  269. /* mov edi, rc (bf imm32) ; mov eax, 231 (b8 e7 00 00 00) ; syscall (0f 05) */
  270. { 0xbf, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xe7, 0x00, 0x00, 0x00, 0x0f, 0x05 },
  271. /* mov ebx, rc (bb imm32) ; mov eax, 252 (b8 fc 00 00 00) ; int 0x80 (cd 80) */
  272. { 0xbb, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xfc, 0x00, 0x00, 0x00, 0xcd, 0x80 },
  273. };
  274. static void build_payload(unsigned char *buf, int is64, int code)
  275. {
  276. uint32_t rc = (uint32_t)code;
  277. memcpy(buf, payload_tmpl[is64 ? 0 : 1], PAYLOAD_LEN);
  278. memcpy(buf + 1, &rc, 4); /* immediate of "mov reg, imm32" */
  279. }
  280. /* ---- injection ------------------------------------------------------------ */
  281. static void cleanup(pid_t pid)
  282. {
  283. if (state.memory_patched)
  284. restore_memory(pid, state.win_addr, state.win_words, state.saved_words);
  285. if (state.regs_set)
  286. (void)ptrace(PTRACE_SETREGS, pid, NULL, &state.regs);
  287. if (state.attached)
  288. (void)ptrace(PTRACE_DETACH, pid, NULL, NULL);
  289. }
  290. static int inject(pid_t pid, int code)
  291. {
  292. struct user_regs_struct regs;
  293. unsigned char payload[PAYLOAD_LEN];
  294. uint64_t patch_addr;
  295. int status, is64;
  296. memset(&state, 0, sizeof(state));
  297. /* 1. Attach and stop the target. */
  298. if (ptrace(PTRACE_SEIZE, pid, NULL, NULL) == 0) {
  299. state.attached = 1;
  300. if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL) != 0) {
  301. if (errno == ESRCH)
  302. goto gone;
  303. perr("interrupting pid %ld failed", (long)pid);
  304. goto fail;
  305. }
  306. } else if (errno == EINVAL) {
  307. /* Kernel without PTRACE_SEIZE (pre-3.4); plain attach will do. */
  308. if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0)
  309. goto attach_failed;
  310. state.attached = 1;
  311. } else {
  312. goto attach_failed;
  313. }
  314. if (waitpid(pid, &status, __WALL) < 0) {
  315. perr("waiting for pid %ld failed", (long)pid);
  316. goto fail;
  317. }
  318. if (!WIFSTOPPED(status))
  319. goto gone;
  320. /* 2. Grab the registers (we need the original set for rollback and the
  321. * segment selector to tell 32-bit compat targets apart). */
  322. if (ptrace(PTRACE_GETREGS, pid, NULL, &state.regs) != 0) {
  323. perr("reading registers of pid %ld failed", (long)pid);
  324. goto fail;
  325. }
  326. is64 = (state.regs.cs == 0x33);
  327. /* A thread parked inside execve() would replace its whole address space
  328. * the moment it is resumed, taking the payload with it. Refuse politely. */
  329. if ((is64 && state.regs.orig_rax == 59) ||
  330. (!is64 && state.regs.orig_rax == 11)) {
  331. if (is64 ? (state.regs.rax == (unsigned long long)-ENOSYS)
  332. : ((uint32_t)state.regs.rax == (uint32_t)-ENOSYS)) {
  333. errno = 0;
  334. perr("pid %ld is mid-exec, retry in a moment", (long)pid);
  335. goto fail;
  336. }
  337. }
  338. /* 3. Find the payload site. */
  339. if (read_maps(pid, state.maps, MAP_MAX, &state.nmaps) != 0) {
  340. perr("cannot read /proc/%ld/maps", (long)pid);
  341. goto fail;
  342. }
  343. if (find_patch_addr(pid, state.maps, state.nmaps, is64, &patch_addr) != 0) {
  344. errno = 0;
  345. perr("no usable executable page found in pid %ld", (long)pid);
  346. goto fail;
  347. }
  348. /* 4. Write the payload, hijack RIP, detach. */
  349. build_payload(payload, is64, code);
  350. if (patch_memory(pid, patch_addr, payload, PAYLOAD_LEN,
  351. &state.win_addr, &state.win_words, state.saved_words) != 0) {
  352. perr("writing payload to pid %ld at 0x%llx failed",
  353. (long)pid, (unsigned long long)patch_addr);
  354. goto fail;
  355. }
  356. state.memory_patched = 1;
  357. regs = state.regs;
  358. regs.rip = patch_addr;
  359. /*
  360. * A thread stopped in an interruptible syscall may carry a pending
  361. * ERESTART_* in RAX; the kernel then rewrites the resume RIP (-2, to
  362. * re-execute the syscall instruction) *after* PTRACE_SETREGS, which
  363. * would land two bytes before our payload. Clearing RAX makes the
  364. * kernel skip the restart dance and return to user space exactly at
  365. * the payload.
  366. */
  367. regs.rax = 0;
  368. if (ptrace(PTRACE_SETREGS, pid, NULL, &regs) != 0) {
  369. perr("pointing pid %ld at the payload failed", (long)pid);
  370. goto fail;
  371. }
  372. state.regs_set = 1;
  373. if (ptrace(PTRACE_DETACH, pid, NULL, NULL) != 0) {
  374. perr("detaching from pid %ld failed", (long)pid);
  375. goto fail;
  376. }
  377. state.attached = 0;
  378. state.memory_patched = 0; /* the payload stays; the target dies next */
  379. return 0;
  380. attach_failed:
  381. {
  382. int err = errno;
  383. errno = 0; /* errno text is part of the message already */
  384. if (err == ESRCH)
  385. perr("no process with pid %ld", (long)pid);
  386. else if (err == EPERM)
  387. perr("cannot ptrace pid %ld: %s; need the same uid (yama ptrace_scope 0) "
  388. "or root/CAP_SYS_PTRACE — try 'sudo forcezero %ld'",
  389. (long)pid, strerror(err), (long)pid);
  390. else
  391. perr("ptrace attach to pid %ld failed: %s", (long)pid, strerror(err));
  392. }
  393. return -1;
  394. gone:
  395. fprintf(stderr, "%s: pid %ld already exited, nothing to inject\n", prog, (long)pid);
  396. return 0;
  397. fail:
  398. cleanup(pid);
  399. return -1;
  400. }
  401. /* ---- cli ------------------------------------------------------------------ */
  402. static void usage(FILE *fp)
  403. {
  404. fputs(
  405. "Usage: forcezero <pid> [exit-code]\n"
  406. "\n"
  407. "Force the running process <pid> to exit with <exit-code> (0..255, default 0).\n"
  408. "The injectable payload is built into this executable; nothing is written to\n"
  409. "disk, and the target keeps no trace of the injection in its files.\n"
  410. "\n"
  411. "Examples:\n"
  412. " sudo forcezero 12345 # process 12345 now exits with status 0\n"
  413. " sudo forcezero 12345 3 # ... with status 3\n"
  414. "\n"
  415. "Requires permission to ptrace <pid>: same uid (with yama ptrace_scope 0) or\n"
  416. "root/CAP_SYS_PTRACE; 'sudo forcezero ...' covers the common cases.\n",
  417. fp);
  418. }
  419. int main(int argc, char **argv)
  420. {
  421. pid_t pid;
  422. int code = 0;
  423. char *end;
  424. if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
  425. usage(stdout);
  426. return 0;
  427. }
  428. if (argc < 2 || argc > 3) {
  429. usage(stderr);
  430. return 2;
  431. }
  432. errno = 0;
  433. pid = (pid_t)strtol(argv[1], &end, 10);
  434. if (errno || *end || end == argv[1] || pid <= 0) {
  435. perr("invalid pid '%s'", argv[1]);
  436. return 2;
  437. }
  438. if (argc == 3) {
  439. long c;
  440. errno = 0;
  441. c = strtol(argv[2], &end, 10);
  442. if (errno || *end || end == argv[2] || c < 0 || c > 255) {
  443. perr("exit code must be an integer in 0..255");
  444. return 2;
  445. }
  446. code = (int)c;
  447. }
  448. return inject(pid, code) == 0 ? 0 : 1;
  449. }