Search code examples
ccompiler-constructionprogram-entry-point

how to change the arguments of main function before running it


In the following code, I want to change the arguments of main function without modifying main function.

Recently, I read the code of AFL fuzzing tool, for the forkserver, I do not understand how to pass the parameters to the target program, so I write this simple program.

I have tried to use pipe to redirect stdin and write parameters to stdin, but it does not work.

#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;
}

If "hello" in argv, argc should be 2 and the output should have "1 hello".


Solution

  • Modifying the C Runtime Environment's int argc argument counter before the C int main(int argc, char **argv) method is called. . . - - - = = = (; A NASTY HACK ;) = = = - - - . . .


    I am using:

    • The latest C++ compiler currently available for OS X High Sierra,
    • Installed the currently standard way, using brew.

    To reproduce the results below argc.cpp code listing, either:


    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 Terminal:


    Compilation:

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

    Running with 0 arguments, original 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 
    

    Running with 3 arguments, original 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 
    

    Running , to demonstrate argc reduction capability,

    • after uncommenting line 13 and recompiling (that hardcodes argc = 2) :
            *pc = 2; // uncomment this to see that you can also reduce argc
    

    with the same 3 arguments, as above, original 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 
    

    If the C Runtime Environment is not initialized on your system similar to the listing:

    you may need to either:

    • Research the difference, by comparing your local C Runtime Environment to the GNU sample above, and/or
    • Experimentally modify my code, until it works for you. For example: walk the int pointers before and also !after! the location of argv and see what you find there...

    If you find hacking your current C Runtime Environment too hard, try to:


    Here is how/why this hack works:

    1. The C Runtime Environment function applicable for your setup (there are multiple possibilities) saves the int argc, char **argv, char **envp arguments in some memory locations - these are luckily adjacent in case of my setup.
    2. On my 64 bit system, due to memory alignment rules the int argc address would be just 1 extra sizeof(int) ahead of the memory pointed to by char **argv, that is why the - 2 in the: int * pc = (int *) argv - 2; line is not - 1.

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