如何在C中编写自修改代码?

Ank*_*rVj 27 c self-modifying

我想编写一段不断变化的代码,即使变化微不足道.

例如,可能是类似的东西

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做的原因:

该程序将在实验操作系统上运行,我不能/不知道如何使用从其他语言编译的程序.我需要这样一个代码的真正原因是因为这个代码是在虚拟机上的客户操作系统上运行的.管理程序是一个翻译代码块的二进制翻译器.翻译做了一些优化.它只翻译一次代码块.下次在guest中使用相同的块时,翻译器将使用先前翻译的结果.现在,如果代码被动态修改,那么翻译人员会注意到这一点,并将其先前的翻译标记为陈旧.因此迫使重新翻译相同的代码.这就是我想要实现的目标,迫使翻译人员进行多次翻译.通常,这些块是分支指令(例如跳转指令)之间的指令.我只是认为自修改代码将是实现这一目标的绝佳方式.

Hea*_*utt 12

您可能需要考虑在C中编写虚拟机,您可以在其中构建自己的自修改代码.

如果您希望编写自修改的可执行文件,很大程度上取决于您所针对的操作系统.您可以通过修改内存中的程序映像来实现所需的解决方案.为此,您将获得程序代码字节的内存地址.然后,您可以在此内存范围上操作操作系统保护,允许您修改字节而不会遇到访问冲突或'''SIG_SEGV'''.最后,您将使用指针(可能是'''unsigned char*'''指针,可能'''在RISC机器上'''unsigned long*'''来修改已编译程序的操作码).

关键是您将修改目标体系结构的机器代码.C代码在运行时没有规范格式 - C是编译器的文本输入文件的规范.


Vat*_*ine 9

这是可能的,但它很可能无法移植,您可能不得不与运行代码的只读内存段以及操作系统实施的其他障碍相抗衡.

  • Linux上的mprotect(2)可用于允许写入.mprotect(...,PROT_WRITE | PROT_EXEC)你正在获得的非便携式答案 - 重写函数本身 - 在许多真实世界的系统中肯定是可能的,但它不是基于C中的功能. (7认同)

Lab*_*abo 9

对不起,我回答的有点晚了,但我想我找到了你要找的东西:https://shanetully.com/2013/12/writing-a-self-mutating-x86_64-c-program/

在本文中,它们通过在堆栈中注入程序集来更改常量的值.然后他们通过修改堆栈上函数的内存来执行shellcode.

下面是第一个代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这里的问题是代码通过设置程序集指令来修改自身,这意味着此后它不再是它本来应该的跨平台C - 失去可移植性.所以_quite_回答Q. (3认同)

Cre*_*ney 5

这将是一个良好的开端.C中基本上是Lisp的功能:

http://nakkaya.com/2010/08/24/a-micro-manual-for-lisp-implemented-in-c/


Pil*_*lsy 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;
}
Run Code Online (Sandbox Code Playgroud)

编译和运行程序时的输出是:

1
2
5
20
25
Run Code Online (Sandbox Code Playgroud)

显然,只有x在每次运行时你想要做的事情数量有限时,这才有效.为了使更改持久化(这是"自我修改"所需的一部分),您可能希望将函数指针变量设置为全局变量或静态变量.我不确定我是否真的可以推荐这种方法,因为通常有更简单,更清晰的方法来完成这种事情.

  • 这个代码示例_really_会自我修改吗?不应该修改代码需要写入包含代码的内存位置吗?我的意思是这个代码将编译成通过评估条件调用两个条件之一的东西.但那毕竟是静态代码?不是吗? (5认同)

Jon*_*n M 4

自解释语言(不像 C 那样硬编译和链接)可能更适合这一点。Perl、javascript、PHP 都有一些邪恶的eval()功能,可能适合您的目的。通过它,您可以拥有一串不断修改的代码,然后通过eval().

  • @Alan,相反,提出这样的问题是我们如何“变得”能够做这样的事情的。 (13认同)