Assembly 如何在x86汇编中编写自修改代码

Assembly 如何在x86汇编中编写自修改代码,assembly,x86,jit,vm-implementation,self-modifying,Assembly,X86,Jit,Vm Implementation,Self Modifying,我正在考虑为我最近正在开发的一个爱好虚拟机编写一个JIT编译器。我懂一点汇编(我主要是一个C程序员。我可以阅读大多数汇编,并参考我不懂的操作码,编写一些简单的程序。)但我很难理解我在网上找到的几个自我修改代码的例子 这就是一个这样的例子: 所提供的示例程序在运行时进行了四种不同的修改,但没有一种解释清楚。Linux内核中断被多次使用,没有解释或详细说明。(作者在调用中断之前将数据移到了几个寄存器中。我想他是在传递参数,但这些参数根本没有解释,让读者猜测。) 我要寻找的是自修改程序代码中最简单、最

我正在考虑为我最近正在开发的一个爱好虚拟机编写一个JIT编译器。我懂一点汇编(我主要是一个C程序员。我可以阅读大多数汇编,并参考我不懂的操作码,编写一些简单的程序。)但我很难理解我在网上找到的几个自我修改代码的例子

这就是一个这样的例子:

所提供的示例程序在运行时进行了四种不同的修改,但没有一种解释清楚。Linux内核中断被多次使用,没有解释或详细说明。(作者在调用中断之前将数据移到了几个寄存器中。我想他是在传递参数,但这些参数根本没有解释,让读者猜测。)

我要寻找的是自修改程序代码中最简单、最直接的示例。我可以查看并使用它来了解如何编写x86汇编中的自修改代码,以及它是如何工作的。您是否可以向我提供任何资源,或者您可以提供任何足以证明这一点的示例

我使用NASM作为我的汇编程序


编辑:我还在Linux上运行这段代码。

您也可以查看类似的项目。您给它一个简化的RISC类型机器的代码,它会动态地生成正确的机器

你应该考虑的一个非常现实的问题是与外国图书馆的接口。您可能需要至少支持一些系统级调用/操作,才能使VM发挥作用。Kitsune的建议是让您思考系统级调用的一个良好开端。您可能会使用mprotect来确保所修改的内存成为合法的可执行内存。(@KitsuneYMG)


一些允许调用用C编写的动态库的FFI应该足以隐藏许多特定于操作系统的细节。所有这些问题都会对您的设计产生相当大的影响,因此最好尽早开始考虑这些问题。

我从未编写过自修改代码,尽管我对其工作原理有基本的了解。基本上,你在内存中写下你想要执行的指令,然后跳转到那里。处理器解释您编写指令的字节,并(尝试)执行它们。例如,病毒和反拷贝程序可能使用这种技术。

关于系统调用,您是对的,参数是通过寄存器传递的。有关linux系统调用及其参数的参考信息,请查看。

哇,这比我预期的要痛苦得多。100%的痛苦是linux保护程序不被覆盖和/或执行数据

两种解决方案如下所示。谷歌搜索涉及到很多内容,所以有些简单的放一些指令字节并执行它们是我的,页面大小的保护和对齐是从谷歌搜索中挑选出来的,这是我在这个例子中必须学习的东西

自我修改的代码是直截了当的,如果你把程序或至少两个简单的函数,编译,然后反汇编,你会得到这些指令的操作码。或者使用nasm编译汇编程序块,等等。根据这一点,我确定了将立即数加载到eax中然后返回的操作码

理想情况下,您只需将这些字节放入某个ram中并执行该ram。要让linux做到这一点,您必须更改保护,这意味着您必须向它发送一个在mmap页面上对齐的指针。因此,分配比您需要的更多,在页面边界上的分配中找到对齐的地址,并从该地址进行保护,使用该内存放置操作码,然后执行

第二个例子是编译到程序中的现有函数,同样由于保护机制,您不能简单地指向它并更改字节,您必须取消对它的写保护。因此,您必须使用该地址和足够的字节备份到前面的页面边界调用mprotect,以覆盖要修改的代码。然后,您可以以任何方式更改该函数的字节/操作码(只要不溢出到任何要继续使用的函数中),并执行它。在本例中,您可以看到
fun()
起作用,然后我将其更改为只返回一个值,再次调用它,现在它已被修改

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

unsigned char *testfun;

unsigned int fun ( unsigned int a )
{
    return(a+13);
}

unsigned int fun2 ( void )
{
    return(13);
}

int main ( void )
{
    unsigned int ra;
    unsigned int pagesize;
    unsigned char *ptr;
    unsigned int offset;

    pagesize=getpagesize();
    testfun=malloc(1023+pagesize+1);
    if(testfun==NULL) return(1);
    //need to align the address on a page boundary
    printf("%p\n",testfun);
    testfun = (unsigned char *)(((long)testfun + pagesize-1) & ~(pagesize-1));
    printf("%p\n",testfun);

    if(mprotect(testfun, 1024, PROT_READ|PROT_EXEC|PROT_WRITE))
    {
        printf("mprotect failed\n");
        return(1);
    }

    //400687: b8 0d 00 00 00          mov    $0xd,%eax
    //40068d: c3                      retq

    testfun[ 0]=0xb8;
    testfun[ 1]=0x0d;
    testfun[ 2]=0x00;
    testfun[ 3]=0x00;
    testfun[ 4]=0x00;
    testfun[ 5]=0xc3;

    ra=((unsigned int (*)())testfun)();
    printf("0x%02X\n",ra);


    testfun[ 0]=0xb8;
    testfun[ 1]=0x20;
    testfun[ 2]=0x00;
    testfun[ 3]=0x00;
    testfun[ 4]=0x00;
    testfun[ 5]=0xc3;

    ra=((unsigned int (*)())testfun)();
    printf("0x%02X\n",ra);


    printf("%p\n",fun);
    offset=(unsigned int)(((long)fun)&(pagesize-1));
    ptr=(unsigned char *)((long)fun&(~(pagesize-1)));


    printf("%p 0x%X\n",ptr,offset);

    if(mprotect(ptr, pagesize, PROT_READ|PROT_EXEC|PROT_WRITE))
    {
        printf("mprotect failed\n");
        return(1);
    }

    //for(ra=0;ra&lt;20;ra++) printf("0x%02X,",ptr[offset+ra]); printf("\n");

    ra=4;
    ra=fun(ra);
    printf("0x%02X\n",ra);

    ptr[offset+0]=0xb8;
    ptr[offset+1]=0x22;
    ptr[offset+2]=0x00;
    ptr[offset+3]=0x00;
    ptr[offset+4]=0x00;
    ptr[offset+5]=0xc3;

    ra=4;
    ra=fun(ra);
    printf("0x%02X\n",ra);

    return(0);
}
#包括
#包括
#包括
#包括
未签名字符*testfun;
无符号整数(无符号整数a)
{
返回(a+13);
}
无符号整数fun2(void)
{
返回(13);
}
内部主(空)
{
无符号整数ra;
无符号整型页面大小;
无符号字符*ptr;
无符号整数偏移量;
pagesize=getpagesize();
testfun=malloc(1023+pagesize+1);
if(testfun==NULL)返回(1);
//需要在页面边界上对齐地址
printf(“%p\n”,testfun);
testfun=(无符号字符*)((长)testfun+pagesize-1)和(pagesize-1));
printf(“%p\n”,testfun);
if(mprotect(testfun,1024,PROT_READ | PROT_EXEC | PROT_WRITE))
{
printf(“mprotect失败\n”);
申报表(1);
}
//400687:b8 0d 00 mov$0xd,%eax
//40068d:c3 retq
testfun[0]=0xb8;
testfun[1]=0x0d;
testfun[2]=0x00;
testfun[3]=0x00;
testfun[4]=0x00;
testfun[5]=0xc3;
ra=((无符号整数(*)testfun)();
printf(“0x%02X\n”,ra);
testfun[0]=0xb8;
testfun[1]=0x20;
testfun[2]=0x00;
testfun[3]=0x00;
testfun[4]=0x00;
testfun[5]=0xc3;
ra=((无符号整数(*)testfun)();
printf(“0x%02X\n”,ra);
printf(“%p\n”,fun);
偏移量=(unsigned int)((long)fun)和(pagesize-1));
ptr=(unsigned char*)((long)fun&(~(pagesize-1));
printf(“%p0x%X\n”,ptr,偏移量);
if(保护(ptr、页面大小、保护读取、保护执行、保护写入))
{
printf(“mprotect失败\n”);
申报表(1);
}
//对于(ra=0;ra20;ra++)printf(“0x%02X,”,ptr[offset+ra]);printf(“\n”);
ra=4;
ra=乐趣(ra);
printf(“0x%02X\n”,ra);
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

char buffer [0x2000];
void* bufferp;

char* hola_mundo = "Hola mundo!";
void (*_printf)(const char*,...);

void hola()
{ 
    _printf(hola_mundo);
}

int main ( void )
{
    //Compute the start of the page
    bufferp = (void*)( ((unsigned long)buffer+0x1000) & 0xfffff000 );
    if(mprotect(bufferp, 1024, PROT_READ|PROT_EXEC|PROT_WRITE))
    {
        printf("mprotect failed\n");
        return(1);
    }
    //The printf function has to be called by an exact address
    _printf = printf;

    //Copy the function hola into buffer
    memcpy(bufferp,(void*)hola,60 //Arbitrary size);


    ((void (*)())bufferp)();  

    return(0);
}
.globl f4
.data     

f4:
    pushl %ebp       #standard function start
    movl %esp,%ebp

f:
    movl $1,%eax # moving one to %eax
    movl $0,f+1  # overwriting operand in mov instuction over
                 # the new immediate value is now 0. f+1 is the place
                 # in the program for the first operand.

    popl %ebp    # standard end
    ret
 #include <stdio.h>

 // assembly function f4
 extern int f4();
 int main(void) {
 int i;
 for(i=0;i<6;++i) {
 printf("%d\n",f4());
 }
 return 0;
 }
1
0
0
0
0
0