cself-referenceself-modifying

C: How to change my own program in my program in runtime?


At runtime, either the assembler or machine code (which is it?) should be somewhere in RAM. Can I somehow get access to it, and read or even write to it?

This is just for educational purposes.

So, I just could compile this code. Am I really reading myself here?

#include <stdio.h>
#include <sys/mman.h>

int main() {
    void *p = (void *)main;
    mprotect(p, 4098, PROT_READ | PROT_WRITE | PROT_EXEC);
    printf("Main: %p\n Content: %i", p, *(int *)(p+2));
    unsigned int size = 16;
    for (unsigned int i = 0; i < size; ++i) {
        printf("%i ", *((int *)(p+i)) );
    }
}

Though, if I add

*(int*)p =4;

then it's a segmentation fault.


From the answers, I could construct the following code which modifies itself during runtime:

#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>

void * alignptr(void * ptr, uintptr_t alignment) {
    return (void *)((uintptr_t)ptr & ~(alignment - 1));
}

// pattern is a 0-terminated string
char* find(char *string, unsigned int stringLen, char *pattern) {
    unsigned int iString = 0;
    unsigned int iPattern;
    for (unsigned int iString = 0; iString < stringLen; ++iString) {
        for (iPattern = 0;
            pattern[iPattern] != 0
            && string[iString+iPattern] == pattern[iPattern];
            ++iPattern);
        if (pattern[iPattern] == 0) { return string+iString; }
    }
    return NULL;
}

int main() {
    void *p = alignptr(main, 4096);
    int result = mprotect(p, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    if (result == -1) {
        printf("Error: %s\n", strerror(errno));
    }

    // Correct a part of THIS program directly in RAM
    char programSubcode[12] = {'H','e','l','l','o',
                                ' ','W','o','r','l','t',0};
    char *programCode = (char *)main;
    char *helloWorlt = find(programCode, 1024, programSubcode);
    if (helloWorlt != NULL) {
        helloWorlt[10] = 'd';
    }   
    printf("Hello Worlt\n");
    return 0;
}

This is amazing! Thank you all!


Solution

  • Machine code is loaded into memory. In theory you can read and write it just like any other part of memory your program as access to.

    There can be some roadblocks to doing this in practice. Modern OSes try and limit the data sections of memory to read/write operations but no execution, and machine code sections of memory to read/execute but no writing. This is to try and limit potential security vulnerabilities that come with allowing executing what ever the program feels like putting into memory (like random stuff it might pull down from the Internet).

    Linux provides the mprotect system call to allow some amount of customization for memory protection. Windows provides the SetProcessDEPPolicy system call.

    Edit for updated question

    It looks like you're trying this on Linux, and using mprotect. The code you posted is not checking the return value from mprotect, so you don't know if the call is succeeding or failing. Here is an updated version that checks the return value:

    #include <stdio.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    #include <stdint.h>
    
    void * alignptr(void * ptr, uintptr_t alignment)
    {
        return (void *)((uintptr_t)ptr & ~(alignment - 1));
    }
    
    int main() {
        void *p = alignptr(main, 4096);
        int result = mprotect(p, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    
        if (result == -1) {
            printf("Error: %s\n", strerror(errno));
        }
        printf("Main: %p\n Content: %i", main, *(int *)(main+2));
        unsigned int size = 16;
        for (unsigned int i = 0; i < size; ++i) {
            printf("%i ", *((int *)(main+i)) );
        }
    }  
    

    Note the changes to the length parameter passed to mprotect and the function aligning the pointer to a system page boundary. You'll need to investigate on your specific system. My system has an alignment of 4096 bytes (determined by running getconf PAGE_SIZE) and after aligning the pointer and changing the length parameter to mprotect to the page size this works, and lets you write over your pointer to main.

    As others have said, this is a bad way to dynamically load code. Dynamic libraries, or plugins, are the preferred method.