Search code examples
clinuxgccexpandphp

C Program to Expand and Run PHP


How would I make a GCC C program on Linux that:

  • has a PHP program script inside itself, not as a separate file
  • expands itself out to /tmp
  • shells out to php CLI command line to run the PHP script
  • passes command line arguments of the C program as command line arguments of the PHP program
  • errors out nicely with a message if php CLI is not possible on the system

Solution

  • I want to give props to @Suroot and also this great article: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967

    However, the entire process is like so at Linux command line:

    ...first, you need to be on Linux, PHP for command line (CLI) needs to be enabled, and PHP at command line should not have any errors when you do "php -v" or pass it a test script that you know typically would work at command line.

    ...second, you need to have GCC installed for compiling C programs.

    ...create a working directory and 'cd' into it.

    ...create a file script.php that has this test code inside:

    <?php
    
    $s1 = @ $argv[1];
    $s2 = @ $argv[2];
    $s3 = @ $argv[3];
    
    for($i=0;$i<=10;$i++){
        echo "($s1) ($s2) ($s3)\n";
    }
    

    ...note, this is only our test. You can put your own PHP code in there later on once you've proven that the test works.

    ...note that "script.php" is critical to be named that or the main.c won't work when it hits the _binary_script_php_start line. That through me for a loop at first because I didn't make the mental connection that the variable name is glued in by this process.

    ...create a C code file main.c that has this code inside:

    #include <stdio.h>
    #include <stdlib.h>
    
    extern char _binary_script_php_start;
    extern char _binary_script_php_end;
    
    int main(int argc, char *argv[]) {
        // EXTRACT OUR RESOURCE OBJECT INTO /tmp/test.php
        char *p = &_binary_script_php_start;
        FILE *fp = fopen("/tmp/test.php","wb");
        while ( p != &_binary_script_php_end ) {
            fputc(*p++,fp);
        }
        fclose(fp);
        // NOW READ IN OUR STANDARD ARGUMENTS AND LAUNCH OUR COMMAND
        int i = 1;
        char *cmd = "php /tmp/test.php";
        char *s = NULL;
        asprintf(&s, "%s",cmd);
        for(i = 1; i < argc; i++) {
            asprintf(&s, "%s \"%s\"",s,argv[i]);
        }
        system(s);
        free(s);
        unlink("/tmp/test.php"); // comment me out for debugging if you want
    }
    

    ...note, I'm sort of rusty with C (haven't used it since the 1980s), and strcpy() is not advised because it's unsafe. So, I read up on StackOverflow that we should use asprintf() on GNU Linux systems. Evidently asprintf() handles buffer overflow and malloc issues? I sure hope so -- I don't want to lead you wrong here. Please research that!

    ...and now the magic begins!

    ...we need to embed the script.php into an object file "data.o" with this command-line command (thus starting with the $ prompt):

    $ objcopy -I binary -O elf32-i386 -B i386 script.php data.o
    

    ...now we build our command "runme" where we link in "data.o" and "main.c" together:

    $ gcc main.c data.o -o runme
    

    ...this then made a compiled C program for us called "runme". We can then execute it to test it:

    $ ./runme "test1" "test2 test3" "test4"
    

    ...You should see an output like the following:

    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    (test1) (test2 test3) (test4)
    

    ...as you can see, the doublequotes actually are important -- they tell the C, and therefore the PHP, that the parameters should be grouped together. That's why test3 doesn't appear in the last parentheses. Without the doublequotes, you'll see a completely different result.

    At this point, you see how we can make a C program that has an embedded PHP script inside and which runs on Linux. (Mac, Windows -- you'll likely need to tweak this code somewhat.) The C program expands our PHP script into /tmp, executes it, and passes it parameters that we can read in and use. From here on out, you can revise the script.php to do as you need, and you can see about using a unique filename so that you don't clobber someone else's /tmp/test.php file, just in case.