C++ Make a Program Write Over Itself
Asked Answered
A

1

1

I posted a question on a similar topic a couple days ago (and one a couple years ago), but I decided to go ahead and get started. I am trying to inject C++ code into C++ code (in a somewhat portable manner using no os specific features and trying to be compiler/toolchain independent manner). I basically want to do this in an attempt to do runtime C++ scripts. I wrote a small test program (its really just kinda thrown together and hacky): Main.cpp:

#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <sstream>
#include <vector>
#include <tuple>

constexpr char hexmap[] = { '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

std::string HexStr( unsigned char *data, int len )
{
    std::string s( len * 2, ' ' );
    for( int i = 0; i < len; ++i ) {
        s[ 2 * i ] = hexmap[ ( data[ i ] & 0xF0 ) >> 4 ];
        s[ 2 * i + 1 ] = hexmap[ data[ i ] & 0x0F ];
    }
    return s;
}
/*I am aware there is a standard GC and that this is 
by no means production.*/
template< typename T, unsigned short ARRAY >
struct GarbageCollector
{
    std::vector< T* > ts;
    GarbageCollector() = default;
    ~GarbageCollector() {
        for( T* i : ts )
            delete i;
    }
};

template< typename T >
struct GarbageCollector< T, 1 >
{
    std::vector< T* > ts;
    GarbageCollector() = default;
    ~GarbageCollector() {
        for( T* i : ts )
            delete[] i;
    }
};


std::tuple< char*, std::streamoff > ReadBinaryBuffer( 
        std::string fileName, GarbageCollector< char, 1 >* gc )
{
    std::ifstream binaryData;
    binaryData.open( "Source.obj", std::ios::binary );
    if( binaryData.fail() ) {
        std::cerr << "Failed to open file!\n";
        return { "Failed to open file!\n", 1 };
    }
    binaryData.seekg( 0, std::ios::end );
    std::streamoff i = binaryData.tellg();
    char* buffer = new char[ i ];
    binaryData.seekg( 0, std::ios::beg );
    binaryData.read( buffer, i );
    binaryData.close();
    gc->ts.push_back( buffer );
    return { buffer, i };
}

std::string ReadBinary( std::string fileName )
{
    GarbageCollector< char, 1 > gc;
    auto result = ReadBinaryBuffer( fileName, &gc );
    std::string stringBuffer;
    stringBuffer.assign( std::get< 0 >( result ), std::get< 1 >( result ) );
    return stringBuffer;
}
std::string ReadBinary( std::tuple< char*, 
        std::streamoff > bufferContainer )
{
    std::string stringBuffer;
    stringBuffer.assign( std::get< 0 >( bufferContainer ), 
            std::get< 1 >( bufferContainer ) );
    return stringBuffer;
}
extern "C"
{
    int test() {
        return 3;
    }
    int( *cmpp )();
}
int main( int argc, char* args )
{
    cmpp = &test;
    auto binary = ReadBinary( "Source.obj" );
    auto function = binary.substr( 347, 56 );
    const char* code = function.c_str();
    std::cout << HexStr( ( unsigned char* ) ( code ), function.size() );
    //strcpy( ( char* )cmpp, ( ( char* ) code ) );
    char* testp = ( char* ) cmpp;
    char* testpp = ( char* ) code;
    for( size_t i = 0; i < 54; ++i ) {
        *testp++ = *testpp++;
    }
    cmpp();
    char close;
    std::cin >> close;
    return 0;
}

Source.cpp:

extern "C"
{
    int calc()
    {
        int q = 30 * 123;
        for( int i = 0; i < 10; ++i )
            q *= i;
        return q;
    }
}

Basically I tried just allocating a hunk of memory with malloc and new, but I thought maybe I could overwrite memory already dedicated to process memory (which is why I have the function test pointed at by cmpp and try to overwrite it). However I get a write access error. I took a look at this post and from one of the answers seems that it is possible to overwrite the programs own memory without an access violation (which is what I want to do), by mistake no less. Could someone elaborate on this please and tell me how to do this in possible a somewhat portable manner without using any non - standard feature (or at least one that can be/is abstracted away)?

Analysand answered 4/5, 2018 at 1:14 Comment(0)
F
2

By default, your program will be loaded into read and execute only memory, no writing allowed (at least on any modern operating system). The different protections are for security reasons, if someone breaks your software, they shouldn't be able to do this to you and for example leak information.

This request is stored inside the binary and is done by the linker. You may be able to change your linker to request for your program to be loaded into writable memory, but this would be far from optimal and also not portable.

A better approach would be to request an executable and writable page from your operating system (mmap et al on linux), but there is no portable way to do this either as far as I am aware.

Fold answered 4/5, 2018 at 1:20 Comment(2)
Thank you for the response I appreciate it! :) What about on a program that is a single translation unit? The linker wont be involved correct?Analysand
The linker will still be used. There is external code like your C runtime (glibc on gcc, VC++ Runtime on Windows etc) and link any readonly data into a page with the appropriate permissions, etc. You can still do this by requesting that .text will be writable by asking the linker, but since it won't ever increase in size, if you want more space you will have to allocate it later anyways.Fold

© 2022 - 2024 — McMap. All rights reserved.