【问题标题】:how to change the arguments of main function before running it如何在运行之前更改主函数的参数
【发布时间】:2019-06-20 03:12:15
【问题描述】:

在下面的代码中,我想在不修改主函数的情况下更改主函数的参数。

最近看了AFL fuzzing工具的代码,对于forkserver,不明白怎么给目标程序传参数,所以写了这个简单的程序。

我曾尝试使用管道重定向stdin并将参数写入stdin,但不起作用。

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

__attribute__((constructor))
void __start() {
    static unsigned char tmp[4];
    int count = 0;
    pid_t pid;
    int status;
    while(count < 3){
        pid = fork();
        if(pid < 0) _exit(1);
        if(!pid){
            //how to pass "hello" to main without modfiying main function.
            return;
        }

        count++;
        if(count == 3) _exit(1);
    }
    //_exit(0);

}

int main(int argc, char** argv){
  for(int i = 0; i < argc; i++){
    printf("%d %s\n", i, argv[i]);
  }
  return 0;
}

如果 argv 中为“hello”,则 argc 应为 2,输出应为“1 hello”。

【问题讨论】:

  • 简单的解决方案:在你分叉后调用execvp(或其他一些exec*)。您不需要在分叉后将标准输入重定向到标准输入,但您可能希望将其重定向到 其他东西,因为具有相同标准输入的多个分叉子代不起作用并且多个分叉子代共享相同的stdout 和/或stderr 往往会导致不可读的交错输出。
  • 系统调用exexvp(或其他exec*)用于执行程序。在我的程序中,__start函数在main之前运行,所以当我们想多次运行实际程序--main()时,不需要像afl-fuzz的forkserver那样多次加载和初始化。跨度>
  • 谢谢! @MichaelBurr 这两个链接很有用。虽然我们可以在没有 argc 的情况下解析 argv,但一些实际程序也需要 argc 来解析 argv。如果我想把 start 函数打成二进制文件,但是像上面的 main 函数一样需要 argc,那么如何在 start 中动态改变 main 的 argc 和 argv 呢?
  • 从您准备将__start 添加到您的二进制文件的事实来看,我假设当您说“不修改主文件”时,您仍然准备修改二进制文件。这是真的吗?

标签: c compiler-construction main


【解决方案1】:

修改 C 运行时环境的 int argc 参数计数器调用 C int main(int argc, char **argv) 方法之前。 . . - - - = = = (; A NASTY HACK ;) = = = - - - 。 . .


我正在使用:

  • 目前可用于 OS X High Sierra 的最新 C++ 编译器,
  • 以当前标准方式安装,使用brew

要重现argc.cpp 代码列表下方的结果,可以:


argc.cpp:

    #include <stdio.h>

    __attribute__((constructor)) void start(int argc, char **argv)
    {
        int * pc = (int *) argv - 2; // NASTY HACK TO GET C RUNTIME argc * ;)
        printf("argc = %d \n", *pc); // the original argc, on most systems ;)
        int NUMBER_OF_PARAMETERS_NEEDED_FOR_FUZZING = 1; // Replace this line 
        // with the simple/complex logic needed for fuzz testing (fuzzing)
        if(!(argc > NUMBER_OF_PARAMETERS_NEEDED_FOR_FUZZING)){ 
            argc = NUMBER_OF_PARAMETERS_NEEDED_FOR_FUZZING + 1;
            *pc = argc; // NASTY HACK TO OVERWRITE C RUNTIME argc ;)
        } 
        // *pc = 2; // uncomment this to see that you can also reduce argc
        argv[1] = "hello"; // Example setting of a fuzzy argument
        // Add more lines, a loop, etc... here to set more fuzzy arguments 
        for (int i = 0; i < argc; i++) {
            printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
        }
        printf("argc = %d \n", argc); // the possibly modified argc
    }

    int main(int argc, char **argv)
    {
        for (int i = 0; i < argc; i++) {
            printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
        }
        printf("argc = %d \n", argc); // the possibly modified argc
        return 0;
    }

Mac OS 终端:


编译:

    $ /usr/local/bin/c++-9 argc.cpp -o argc

运行,带有0 参数,原始int argc = 1

    $ ./argc
    argc = 1 
    start: argv[0] = './argc'
    start: argv[1] = 'hello'
    argc = 2 
    main: argv[0] = './argc'
    main: argv[1] = 'hello'
    argc = 2 

运行,带有3 参数,原始int argc = 4

    $ ./argc 1 2 3
    argc = 4 
    start: argv[0] = './argc'
    start: argv[1] = 'hello'
    start: argv[2] = '2'
    start: argv[3] = '3'
    argc = 4 
    main: argv[0] = './argc'
    main: argv[1] = 'hello'
    main: argv[2] = '2'
    main: argv[3] = '3'
    argc = 4 

运行,展示argc的还原能力,

  • 取消注释第 13 行并重新编译后(硬编码argc = 2):
        *pc = 2; // uncomment this to see that you can also reduce argc

使用相同的3 参数,如上,原始int argc = 4

    $ ./argc 1 2 3
    argc = 4 
    start: argv[0] = './argc'
    start: argv[1] = 'hello'
    start: argv[2] = '2'
    start: argv[3] = '3'
    argc = 4 
    main: argv[0] = './argc'
    main: argv[1] = 'hello'
    argc = 2 

如果 C 运行时环境在您的系统上初始化,类似于清单:

您可能需要:

  • 通过将您的本地 C 运行时环境与上面的 GNU 示例进行比较来研究差异,和/或
  • 实验性地修改我的代码,直到它适合你。例如:遍历 int 指针 before 以及 !after! argv 的位置,看看会发生什么你在那里找到...

如果您觉得破解当前的 C 运行时环境太难了,请尝试:


以下是此 hack 的工作方式/原因:

  1. 适用于您的设置的C Runtime Environment 函数(有多种可能性)将int argc, char **argv, char **envp 参数保存在某些内存位置 - 幸运的是,这些位置在我的设置中是相邻的。
  2. 在我的64 bit 系统上,由于内存对齐规则,int argc 地址将仅比char **argv 指向的内存多1 个sizeof(int),这就是为什么- 2 在:@ 987654350@ 行不是- 1

╦ ╦  ╔═╗  ╔═╗  ╔═╗  ╦ ╦       ╦ ╦  ╔═╗  ╔═╗  ╦╔═  ╦  ╔╗╔  ╔═╗ ╦ ╦ ╦
╠═╣  ╠═╣  ╠═╝  ╠═╝  ╚╦╝  ───  ╠═╣  ╠═╣  ║    ╠╩╗  ║  ║║║  ║ ╦ ║ ║ ║
╩ ╩  ╩ ╩  ╩    ╩     ╩        ╩ ╩  ╩ ╩  ╚═╝  ╩ ╩  ╩  ╝╚╝  ╚═╝ o o o

【讨论】:

  • 谢谢!但是,我不明白为什么 start 和 main 之间不共享“argc”。如果我们不能修改 main 函数,输出不会退出“1 hello”。在某些场景下,例如二进制文件的instrument,我们不能修改二进制文件来改变它的'argc'。
  • 'argc'不是指针,所以我们在start函数中修改它,不会影响main函数'argc'的实际值。有没有办法做到这一点?
  • 感谢您的回答。这很有帮助。但是,我无法在使用 x86_64 ubuntu 系统的计算机上重现它。我已经尝试过从(int *) argv - 1 到(int *) argv - 12 的地址,argc 的值没有改变,但它是我破解它的一个很好的起点。
  • 太好了,我很高兴,请尝试探索 argv 之后的 int 指针,(+2、+4 等...)如果您发现破解当前 C 运行时环境太难,请尝试在 Ubuntu 64 上安装 GNU 编译器:[help.ubuntu.com/community/InstallingCompilers]。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多