在x86中,你不能直接使用rip
作为mov
的源或目的地。如果要更改rip
,必须使用jmp
、call
或ret
。
尝试将地址移动到rax
,然后使用jmp rax
。确保将rax
标记为“clobbered”。
不过,你所尝试的方法更容易的方式是简单地创建一个函数指针,将其指向你分配的内存,并调用它。
请注意,你使用malloc
分配的内存不会被标记为可执行,因此测试将立即崩溃。为了解决这个问题,你可以使用mprotect(2)
更改页面权限。但是这样会更改整个页面的权限。更好的解决方案是使用mmap(2)
映射匿名、可执行页面。
首先,我们编写一个小的汇编函数:
prog.s
[BITS 64]
global addints
addints:
mov rax, rdi
add rax, rsi
ret
然后将其组装起来,并查看操作码:
$ nasm -o binary prog.s
$ ndisasm -b64 prog
00000000 4889F8 mov rax,rdi
00000003 4801F0 add rax,rsi
00000006 C3 ret
现在将其编码到我们的示例程序中:
mmap.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#define PAGE_SIZE 0x1000
static const uint8_t mycode[] = {
0x48, 0x89, 0xF8,
0x48, 0x01, 0xF0,
0xC3,
};
int main(void)
{
void *m;
m = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (m == MAP_FAILED) {
fprintf(stderr, "mmap() failed: %m\n");
exit(2);
}
memcpy(m, mycode, sizeof(mycode));
mprotect(m, PAGE_SIZE, PROT_READ | PROT_EXEC);
unsigned long (*func)(unsigned long x, unsigned long y) = m;
unsigned long result = func(7, 3);
printf("Result: %lu\n", result);
return 0;
}
试一试吧:
$ gcc -Wall -Werror -o mmap mmap.c
$ ./mmap
Result: 10