logo

mstrace

Small Linux strace(1) implementationgit clone https://anongit.hacktivis.me/git/mstrace.git/

mstrace.c (3329B)


  1. // SPDX-FileCopyrightText: 2024 Haelwenn (lanodan) Monnier <contact+mstrace@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _DEFAULT_SOURCE
  4. #include <errno.h>
  5. #include <inttypes.h> // PRIu64
  6. #include <linux/ptrace.h> // ptrace_syscall_info, __u64
  7. #include <stdbool.h>
  8. #include <stdio.h> // fprintf
  9. #include <string.h> // strerror
  10. #include <sys/ptrace.h> // ptrace()
  11. #include <sys/syscall.h> // SYS_*
  12. #include <sys/wait.h>
  13. #include <unistd.h> // getpid, fork, execvp
  14. extern void print_syscall_entry(__u64 nr, __u64 args[6]);
  15. extern void print_syscall_exit(__u64 nr, __u64 args[6]);
  16. pid_t child = -1;
  17. static int
  18. wait_for_syscall(int *status)
  19. {
  20. while(1)
  21. {
  22. ptrace(PTRACE_SYSCALL, child, 0, 0);
  23. waitpid(child, status, 0);
  24. if(WIFSTOPPED(*status) && WSTOPSIG(*status) & 0x80) return 0;
  25. if(WIFEXITED(*status)) return 1;
  26. }
  27. }
  28. int
  29. main(int argc, char *argv[])
  30. {
  31. argc -= 1;
  32. argv += 1;
  33. if(argc < 1)
  34. {
  35. fprintf(stderr, "Usage: mstrace <command> [args...]\n");
  36. return 1;
  37. }
  38. child = fork();
  39. if(child < 0)
  40. {
  41. fprintf(stderr, "mstrace: error: Failed creating child process: %s\n", strerror(errno));
  42. return 1;
  43. }
  44. else if(child == 0)
  45. {
  46. errno = 0;
  47. int ret = ptrace(PTRACE_TRACEME);
  48. if(ret < 0 && errno != 0)
  49. {
  50. fprintf(stderr, "mstrace: error: Failed ptrace(PTRACE_TRACEME): %s\n", strerror(errno));
  51. return 1;
  52. }
  53. if(kill(getpid(), SIGSTOP) < 0)
  54. {
  55. fprintf(stderr, "mstrace: error: Failed stopping parent: %s\n", strerror(errno));
  56. return 1;
  57. }
  58. execvp(argv[0], argv);
  59. return 1;
  60. }
  61. struct
  62. {
  63. __u64 nr;
  64. __u64 args[6];
  65. } entry;
  66. entry.nr = 0;
  67. int status;
  68. waitpid(child, &status, 0);
  69. ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
  70. while(1)
  71. {
  72. if(wait_for_syscall(&status) != 0) break;
  73. struct ptrace_syscall_info syscall_info;
  74. long ret =
  75. ptrace(PTRACE_GET_SYSCALL_INFO, child, sizeof(struct ptrace_syscall_info), &syscall_info);
  76. if(ret < 0)
  77. {
  78. fprintf(
  79. stderr,
  80. "mstrace: error: Failed getting syscall information via PTRACE_GET_SYSCALL_INFO: %s\n",
  81. strerror(errno));
  82. continue;
  83. }
  84. switch(syscall_info.op)
  85. {
  86. case PTRACE_SYSCALL_INFO_ENTRY:
  87. entry.nr = syscall_info.entry.nr;
  88. for(int i = 0; i < 6; i++)
  89. entry.args[i] = syscall_info.entry.args[i];
  90. print_syscall_entry(entry.nr, entry.args);
  91. break;
  92. case PTRACE_SYSCALL_INFO_EXIT:
  93. if(entry.nr != SYS_execve) print_syscall_exit(entry.nr, entry.args);
  94. if(syscall_info.exit.is_error)
  95. {
  96. fprintf(stderr,
  97. " = (errno: %" PRIi64 ") %s\n",
  98. (int64_t)(-syscall_info.exit.rval),
  99. strerror(-syscall_info.exit.rval));
  100. }
  101. else
  102. {
  103. fprintf(stderr, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval);
  104. }
  105. entry.nr = 0;
  106. break;
  107. case PTRACE_SYSCALL_INFO_SECCOMP:
  108. print_syscall_exit(syscall_info.seccomp.nr, syscall_info.seccomp.args);
  109. // TODO: Check how strace manages this
  110. fprintf(stderr, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data);
  111. break;
  112. case PTRACE_SYSCALL_INFO_NONE:
  113. fprintf(stderr, "[syscall-info] none\n");
  114. break;
  115. }
  116. }
  117. if(entry.nr != 0)
  118. print_syscall_exit(entry.nr, entry.args);
  119. if(WIFEXITED(status))
  120. {
  121. fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
  122. return WEXITSTATUS(status);
  123. }
  124. return 0;
  125. }