logo

mstrace

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

mstrace.c (3641B)


  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(__u64 nr, __u64 args[6]);
  15. pid_t child = -1;
  16. static int
  17. wait_for_syscall(int *status)
  18. {
  19. while(1)
  20. {
  21. ptrace(PTRACE_SYSCALL, child, 0, 0);
  22. waitpid(child, status, 0);
  23. if(WIFSTOPPED(*status) && WSTOPSIG(*status) & 0x80) return 0;
  24. if(WIFEXITED(*status)) return 1;
  25. }
  26. }
  27. int
  28. main(int argc, char *argv[])
  29. {
  30. argc -= 1;
  31. argv += 1;
  32. if(argc < 1)
  33. {
  34. fprintf(stderr, "Usage: mstrace <command> [args...]\n");
  35. return 1;
  36. }
  37. child = fork();
  38. if(child < 0)
  39. {
  40. fprintf(stderr, "mstrace: error: Failed creating child process: %s\n", strerror(errno));
  41. return 1;
  42. }
  43. else if(child == 0)
  44. {
  45. errno = 0;
  46. int ret = ptrace(PTRACE_TRACEME);
  47. if(ret < 0 && errno != 0)
  48. {
  49. fprintf(stderr, "mstrace: error: Failed ptrace(PTRACE_TRACEME): %s\n", strerror(errno));
  50. return 1;
  51. }
  52. if(kill(getpid(), SIGSTOP) < 0)
  53. {
  54. fprintf(stderr, "mstrace: error: Failed stopping parent: %s\n", strerror(errno));
  55. return 1;
  56. }
  57. execvp(argv[0], argv);
  58. return 1;
  59. }
  60. bool neednl = false;
  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 execve(2) at entry time because parameters passed gets cleaned up
  91. // meanwhile getcwd(2) pass a buffer and so needs to be printed at return time
  92. if(entry.nr == SYS_execve)
  93. {
  94. print_syscall(entry.nr, entry.args);
  95. neednl = true;
  96. }
  97. break;
  98. case PTRACE_SYSCALL_INFO_EXIT:
  99. if(entry.nr != SYS_execve) print_syscall(entry.nr, entry.args);
  100. if(syscall_info.exit.is_error)
  101. {
  102. fprintf(stderr,
  103. " = (errno: %" PRIi64 ") %s\n",
  104. (int64_t)(-syscall_info.exit.rval),
  105. strerror(-syscall_info.exit.rval));
  106. }
  107. else
  108. {
  109. fprintf(stderr, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval);
  110. }
  111. neednl = false;
  112. entry.nr = 0;
  113. break;
  114. case PTRACE_SYSCALL_INFO_SECCOMP:
  115. print_syscall(syscall_info.seccomp.nr, syscall_info.seccomp.args);
  116. neednl = true;
  117. // TODO: Check how strace manages this
  118. fprintf(stderr, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data);
  119. break;
  120. case PTRACE_SYSCALL_INFO_NONE:
  121. if(neednl) fprintf(stderr, "\n");
  122. fprintf(stderr, "[syscall-info] none\n");
  123. neednl = false;
  124. break;
  125. }
  126. }
  127. if(entry.nr != 0)
  128. {
  129. print_syscall(entry.nr, entry.args);
  130. neednl = true;
  131. }
  132. if(neednl) fprintf(stderr, "\n");
  133. if(WIFEXITED(status))
  134. {
  135. fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
  136. return WEXITSTATUS(status);
  137. }
  138. return 0;
  139. }