Подтвердить что ты не робот

Как написать собственный код модификации в C?

Я хочу написать фрагмент кода, который постоянно изменяется, даже если это изменение незначительно.

Например, может быть что-то вроде


for i in 1 to  100, do 
begin
   x := 200
   for j in 200 downto 1, do
    begin
       do something
    end
end

Предположим, что я хочу, чтобы мой код после первой итерации изменил строку x := 200 на некоторую другую строку x := 199, а затем после следующей итерации измените ее на x := 198 и так далее.

Возможно ли писать такой код? Должен ли я использовать встроенную сборку для этого?

ИЗМЕНИТЬ: Вот почему я хочу сделать это в C:

Эта программа будет запущена в экспериментальной операционной системе, и я не могу/не знаю, как использовать программы, скомпилированные с других языков. Настоящая причина, по которой мне нужен такой код, заключается в том, что этот код запускается в гостевой операционной системе на виртуальной машине. Гипервизор - это двоичный переводчик, переводящий куски кода. Переводчик выполняет некоторые оптимизации. Он только переводит куски кода один раз. В следующий раз, когда тот же кусок используется в гостевой, переводчик будет использовать ранее переведенный результат. Теперь, если код изменен "на лету", тогда переводчик замечает это и отмечает, что его предыдущий перевод был устаревшим. Таким образом, заставляя повторный перевод одного и того же кода. Этого я хочу достичь, чтобы заставить переводчика сделать много переводов. Обычно эти куски являются инструкциями между инструкциями на ветки (например, инструкциями перехода). Я просто думаю, что самомодифицирующий код будет фантастическим способом достижения этого.

4b9b3361

Ответ 1

Возможно, но это, скорее всего, невозможно по возможности и возможно вам придется бороться с сегментами памяти только для чтения для выполняемого кода и другими препятствиями, установленными вашей ОС.

Ответ 2

Возможно, вам захочется написать виртуальную машину на C, где вы можете создать свой собственный самомодифицирующийся код.

Если вы хотите написать самомодифицирующиеся исполняемые файлы, многое зависит от операционной системы, на которую вы нацеливаете. Вы можете подойти к своему желаемому решению, изменив образ программы в памяти. Для этого вы получите адрес памяти вашего программного кода. Затем вы можете манипулировать защитой операционной системы в этом диапазоне памяти, позволяя изменять байты, не сталкиваясь с нарушением доступа или "SIG_SEGV". Наконец, вы должны использовать указатели (возможно, '' unsigned char * '' 'указатели, возможно,' '' unsigned long * '' ', как на машинах RISC), чтобы изменить коды операций скомпилированной программы.

Ключевым моментом является то, что вы будете изменять машинный код целевой архитектуры. Канонический формат для кода C во время его работы - C - спецификация текстового входного файла для компилятора.

Ответ 4

Извините, я немного опоздал, но я думаю, что нашел именно то, что вы ищете: https://shanetully.com/2013/12/writing-a-self-mutating-x86_64-c-program/

В этой статье они изменяют значение константы, вставляя сборку в стек. Затем они выполняют шеллкод, изменяя память функции в стеке.

Ниже приведен первый код:

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

void foo(void);
int change_page_permissions_of_address(void *addr);

int main(void) {
    void *foo_addr = (void*)foo;

    // Change the permissions of the page that contains foo() to read, write, and execute
    // This assumes that foo() is fully contained by a single page
    if(change_page_permissions_of_address(foo_addr) == -1) {
        fprintf(stderr, "Error while changing page permissions of foo(): %s\n", strerror(errno));
        return 1;
    }

    // Call the unmodified foo()
    puts("Calling foo...");
    foo();

    // Change the immediate value in the addl instruction in foo() to 42
    unsigned char *instruction = (unsigned char*)foo_addr + 18;
    *instruction = 0x2A;

    // Call the modified foo()
    puts("Calling foo...");
    foo();

    return 0;
}

void foo(void) {
    int i=0;
    i++;
    printf("i: %d\n", i);
}

int change_page_permissions_of_address(void *addr) {
    // Move the pointer to the page boundary
    int page_size = getpagesize();
    addr -= (unsigned long)addr % page_size;

    if(mprotect(addr, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
        return -1;
    }

    return 0;
}

Ответ 5

В зависимости от того, насколько вам нужна свобода, вы можете выполнить то, что хотите, с помощью указателей функций. Используя псевдокод в качестве точки перехода, рассмотрим случай, когда мы хотим изменить эту переменную x по-разному с изменением индекса цикла i. Мы могли бы сделать что-то вроде этого:

#include <stdio.h>

void multiply_x (int * x, int multiplier)
{
    *x *= multiplier;
}

void add_to_x (int * x, int increment)
{
    *x += increment;
}

int main (void)
{
    int x = 0;
    int i;

    void (*fp)(int *, int);

    for (i = 1; i < 6; ++i) {
            fp = (i % 2) ? add_to_x : multiply_x;

            fp(&x, i);

            printf("%d\n", x);
    }

    return 0;
}

Вывод при компиляции и запуске программы:

1
2
5
20
25

Очевидно, что это будет работать, только если у вас есть конечное количество вещей, которые вы хотите сделать с x при каждом прохождении. Чтобы сделать изменения постоянными (что является частью того, что вы хотите от "самомодификации" ), вам нужно сделать переменную-указатель функции глобальной или статической. Я не уверен, что действительно могу рекомендовать этот подход, потому что часто есть более простые и понятные способы решения такого рода вещей.

Ответ 6

Предложение о внедрении LISP в C, а затем использование этого является твердым, из-за проблем с переносимостью. Но если вы действительно этого хотели, это также может быть реализовано в другом направлении на многих системах, загрузив байт-код программы в память и затем вернувшись к нему.

Есть несколько способов, которыми вы могли бы это сделать. Один из способов - использование эксплойта переполнения буфера. Другим было бы использование mprotect(), чтобы сделать секцию кода доступной для записи, а затем изменить создаваемые компилятором функции.

Методы, подобные этому, интересны для задач программирования и запутанных соревнований, но учитывая, что нечитаемый ваш код будет сочетаться с тем фактом, что вы используете то, что C считает undefined, их лучше избегать в производственных средах.

Ответ 7

Для этого может быть лучше язык самоинтерпретации (не скомпилированный и связанный с C). Perl, javascript, PHP имеют функцию зла eval(), которая может быть подходящей для вашей цели. Благодаря этому вы можете иметь строку кода, которую вы постоянно изменяете, а затем выполняете через eval().