gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 12:18:34 26 4
gpt4 key购买 nike

我正在考虑为我最近一直在研究的一个业余爱好虚拟机编写一个 JIT 编译器。我了解一些汇编语言(我主要是一名 C 程序员。我可以阅读大多数汇编语言并引用我不理解的操作码,并编写一些简单的程序。)但是我很难理解这几个示例我在网上找到的自修改代码。

这是一个这样的例子:http://asm.sourceforge.net/articles/smc.html

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

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

我使用 NASM 作为我的汇编器。

编辑:我也在 Linux 上运行此代码。

最佳答案

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

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

自修改代码很简单,如果您获取程序或至少只是两个简单的函数,编译然后反汇编,您将获得这些指令的操作码。或者使用 nasm 编译汇编程序 block 等。由此我确定了将立即数加载到 eax 中然后返回的操作码。

理想情况下,您只需将这些字节放入某个内存中并执行该内存即可。要让 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);
}

关于assembly - 如何在x86汇编中编写自修改代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4812869/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com