【问题标题】:correct way to filter execve environment过滤 execve 环境的正确方法
【发布时间】:2018-05-19 14:24:49
【问题描述】:

我正在尝试编写一个LD_PRELOADable 库,以防止进程从该变量中删除自身(以确保子代继承它)。 到目前为止,我成功地包装了 putenvsetenvclearenv,但 execve 给我带来了问题。

到目前为止我的代码:

int (*real_execve)(const char *filename, char *const argv[], char *const envp[]);
int execve(const char *filename, char *const argv[], char *const envp[]){
  real_execve = dlsym(RTLD_NEXT,"execve");
  char *path = getenv("LD_PRELOAD");
  fprintf(stderr, "INTERCEPTED execve, env:\n");
  int i;
  for(i=0;envp[i]!=NULL;i++);
  char *nenvp[i+1];
  nenvp[i]=NULL;
  for(i=0;envp[i]!=NULL;i++){
    char *string = envp[i];
    char *buf = malloc((strlen(string)+1)*sizeof(char));
    strcpy(buf,string);
    char *name = strtok(buf,"=");
    char *value = strtok(NULL,"=");
    if(0==strcmp(name,"LD_PRELOAD")){
      fprintf(stderr,"  FIXING '%s'\n",string);
      char * nstring = malloc((strlen(name)+strlen(path)+strlen(value)+3)*sizeof(char));
      strcpy(nstring,name);
      strcat(nstring,"=");
      strcat(nstring,path);
      strcat(nstring,":");
      strcat(nstring,value);
      nenvp[i]=nstring;
      fprintf(stderr,"    TO  '%s'\n",nenvp[i]);
      free(string);
    }else{
      nenvp[i]=envp[i];
      fprintf(stderr,"  LEFT  '%s'\n",nenvp[i]);
    }
    free(buf);
  }
  fprintf(stderr, "  CALLING %s\n", filename);
  return real_execve(filename,argv,nenvp);
}

我遇到了 2 个问题:

  • 它记录如下内容:

    FIXING 'LD_PRELOAD=/usr/$LIB/libstdc++.so.6 /usr/$LIB/libgcc_s.so.1 /usr/$LIB/libxcb.so.1'
      TO  'LD_PRELOAD=/usr/$LIB/libstdc++.so.6 /usr/$LIB/libgcc_s.so.1 /usr/$LIB/libxcb.so.1:/usr/$LIB/libstdc++.so.6 /usr/$LIB/libgcc_s.so.1 /usr/$LIB/libxcb.so.1'
    

    而不是预期的自我路径前置,所以我想我以某种方式搞砸了 strtok。

  • 我遇到了很多这样的错误:

    Error in 'sh': munmap_chunk(): invalid pointer: 0x00007fff3888af4a

    这听起来像是我释放了太多,但我找不到罪魁祸首。

我希望这听起来不太像“嘿,帮我解决这个问题”的帖子,但我在这里有点碰壁,非常感谢任何帮助。

【问题讨论】:

  • 您需要(重新)阅读strtok 的文档。如果你用相同的字符串和分隔符调用它两次,你会得到相同的结果。第二次和后续调用必须将 NULL 作为其第一个参数。
  • 显然完全扫了一眼。非常感谢你,我会解决这个问题,然后编辑我的问题以反映第二个问题,或者如果它消失了就关闭它(如果你将你的评论改写为答案,我很乐意支持/接受它)。
  • 确定后,在应用该修复程序后,它现在会复制 value 中的值,而不是在其前面添加 path。也仍然收到munmap_chunk 错误
  • 可能不相关,但请记住 execve 应该是异步信号安全的,因此您不能真正从中调用非异步安全函数(mallocstrtok、 ETC。)。法律职能列表见manpage
  • @nonchip 这就是我通常所做的,因为 allocammap 不能保证在所有 Unix 风格上都是异步安全的 (signal-safety(7))。

标签: c environment-variables ld-preload execve


【解决方案1】:

您不能假设envp 中的单个字符串分配有malloc,因此free(string) 可能是未定义的行为。用完全空的堆调用exec* 几乎是不可能的,而且整个图像都会被替换,所以不必担心。

您的第二个strtok 调用应提供NULL 作为其第一个参数。有关说明和示例,请参阅 man strtok

【讨论】:

  • 删除free(string) 摆脱了munmap_chunk 问题,将我的strtok 调用替换为char* sep = strchr(buf,"="); *buf='\0'; name=buf; value=sep+1;(以确保它不会在随后的= 字符处拆分,并且手动拆分字符串)现在使其复制value(中间有:),而不是连接pathvalue。几乎就好像我以某种方式覆盖了路径,但我不这样做。 getenv 是不是很困惑?即使strcpy开头的路径也无济于事
  • 你确定你得到的是“你的”LD_PRELOAD?也许它已经是setenved。也许您的setenv 拦截器有 UB...我会使用调试器来查看发生了什么。
  • 显然它以某种方式被设置了,即使我认为我拦截了它(可能是因为分叉导致我的拦截不再起作用?)。将 getenv 移动到 __attribute__((constructor)) 已修复它,即使显然我的库仍然没有在任何地方加载。
【解决方案2】:

直接执行此操作:

  • 创建一个 new 指针数组,其大小需要保存新的 env/var/s
  • strdup() 从旧数组到新数组所需的所有元素。
  • 根据需要添加新内容。
  • 确保数组中的最后一个指针是NULL
  • 将新的指针数组传递给原来的execve()

不要不要修改甚至(尝试)旧环境的free() 条目。

【讨论】:

  • 这基本上就是现在正在发生的事情(只是没有 strdup,目前重用旧元素,似乎工作得很好)。我唯一不明白的是为什么它仍然没有做好它的工作,但我希望我能在某个时候解决这个问题。有兴趣的可以看看github.com/nonchip/steam_workaround_fsoverflow
  • @nonchip:重要的是单独离开“旧”环境。虽然您很可能不需要strdup(),但可以。但是你绝对不能释放或修改“旧”的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 2012-08-06
  • 2016-02-09
相关资源
最近更新 更多