C - ptrace 和 waitpid 的段错误

标签 c segmentation-fault

对于 C 语言的实验室学校,我们必须编写一个进程(我们将其称为 A),该进程需要附加到另一个进程(B)并在函数中放置陷阱(陷阱指令为 0xCC),因此我们做到了,但是当 B 输入这个函数时,我们遇到了段错误

所以这是附加到其他进程的进程A

#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

int main(int argc, char *argv[]) {

pid_t pidTargetProgram;
FILE *file;
int buf;
char path[50];
long long int address;


if (argc == 2) {
   fprintf(stderr, "Argument waited !\n");
   exit(1);
 }

// The second argument is the PID of the program 
pidTargetProgram = atoi(argv[1]);

// The third argument is the address of the function where we are going to put a trap
address = strtoll(argv[2], NULL, 16);

// We put the path to the file mem of the program 
sprintf(path, "/proc/%s/mem", argv[1]);

// We attach to the other program
if(ptrace(PTRACE_ATTACH, pidTargetProgram, NULL, NULL) < 0) {
  perror("Error with ptrace_attach !");
  exit(1);
}

// We wait it to be synchronize
if (waitpid(pidTargetProgram, NULL, WUNTRACED) < 0) {
  perror("Error with waitpid !\n");
  exit(0);
  }



// We open the file mem in read and write mode
  if ((file = fopen(path, "r+")) == NULL) {
    perror("Error during the opening of mem file from process !");
    exit(1);
  }



// We place our cursor on the address of the function
  fseek(file, address, SEEK_SET);

  char trap[] = {0xCC, 0x00, 0x00, 0x00};

  // We put the trap in the foo function
  if (fwrite(trap, 1, 5, file) < 1) {
    perror("Error to write !");
    exit(1);
    }

  int counter = 0;

  fseek(file, address, SEEK_SET);

  // We print the other function's memory
  while (fread(&buf, 4, 1, file) > 0) {
    printf("Line n°%d : 0x%x\n", counter++, buf);
    }

  // We close the file
  if (fclose(file) != 0) {
    perror("Error during the closing !");
    exit(1);
  }

  // We said to continue to the other program
  if (ptrace(PTRACE_CONT, pidTargetProgram, NULL, NULL) < 0) {
    perror("Error during ptrace_cont !\n");
    exit(1);
    }

  printf("continued !\n");

  // We wait the other program stop
  if (waitpid(pidTargetProgram, NULL, WUNTRACED) < 0) {
    perror("Error with waitpid !\n");
    exit(0);
    }

  printf("Trace declenched !\n");

  // We detach 
  if (ptrace(PTRACE_DETACH, pidTargetProgram, NULL, NULL) < 0) {
    perror("Error during ptrace_detach !");
    exit(1);
    }

  printf("detach success ! \n");

  return 0;
}

这是进程B:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

// Function to execute to take the trap
void foo(){
  int i = 0;
  printf("foo :::: %d", i);
}

int main(int argc, char *argv[]) {

  char text[10];
  pid_t pidProgram;

  // We get the PID
  pidProgram = getpid();

  // We print the PID
  fprintf(stdout, "PID's program : %d\n", pidProgram);

  // We print the address of foo()
  fprintf(stdout, "foo address : %p\n", &(foo));

  // We stop the program to lunch the other program
  fgets(text, 10, stdin);

  int i;
  for(i = 0 ; i < 100 ; i++){
    foo(i);
  }

  return 0;
}

要执行此操作,我们首先启动 B,以便它为我们提供 PID 和地址,并在 fgets 处暂停。 所以在那之后,我们启动程序 A 并给它 PID 和地址,它在第二个 waitpid 处停止。 之后我们继续 B 并写了一些东西,我们有一个段错误并停止。我们不明白为什么,因为在内存中我们可以清楚地看到陷阱(0xCC)并且它不起作用 但是在程序 A 中,我们有 痕迹松弛! 分离成功!

所以 A 上没有错误,但 B 上有段错误

你有什么想法吗? 我们使用 Centos 作为操作系统。 对不起我的英语。

感谢

朱利安

最佳答案

程序按预期工作:

首先,您更改正在运行的进程镜像以在函数 foo 的开头设置 0xcc,这会触发断点/陷阱。

然后,在进程 a 跟踪进程时调用此函数。 所以这个电话

waitpid(pidTargetProgram, NULL, WUNTRACED) < 0) // Process a

返回。现在你从进程 b 中分离出来

ptrace(PTRACE_DETACH, pidTargetProgram, NULL, NULL);

但是,您并没有恢复之前进程中被覆盖的指令!因此,下一条指令已损坏并导致您观察到的段错误。此外,进程在下一条指令 PC+1 处重新启动(紧接在 0xcc 之后),因此您需要使用 PTRACE_GETREGS/PTRACE_SETREGS

将 PC 设置回一个字节

顺便说一句。使用 ptrace 接口(interface)通过 PTRACE_POKETEXT 设置和重置断点指令比使用 /proc/pid/mem 方式更优雅。

TL;DR:您需要先恢复原始指令并在重新启动进程 b 之前重置 PC,然后它应该可以按预期工作。

关于C - ptrace 和 waitpid 的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52854538/

相关文章:

c++ - 如何初始化一个很长的数组?

c - For循环缺少段错误的几个索引

C++ 函数返回指针不会造成段错误

c - 指向二维数组的指针在底层是如何工作的?

c++ - 如何在 C++ 中启动具有管理员权限的应用程序?

c - 如何重复 scanf() n 次?

c - 在结构的动态数组上使用 realloc 时出现段错误

assembly - 段错误 x86 <_dl_debug_state>

c - 从 C 函数返回字符串

c - 从不兼容的指针类型初始化 [默认启用] - 怎么了?