- #include<stdio.h>
- #include <sys/socket.h>
- #include <sys/user.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <inttypes.h>
- #include <sys/reg.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/mman.h>
- #include <sys/personality.h>
- void (*aaa)();
- void null_func()
- {
- printf("THIS is NULL!!!\n");
- }
- void map_and_call_null()
- {
- char *addr = NULL;
- addr = mmap(0, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
- addr[0] = '\xff';
- addr[1] = '\x25';
- *(unsigned int *)&addr[2] = 6;
- *(unsigned long *)&addr[6] = (unsigned long)&null_func;
- aaa = NULL; //...NULL
- (*aaa)();
- int *empty = NULL;
- printf("%x\n",*empty);
- printf("%x\n",*(empty+1));
- printf("%x\n",*(empty+2));
- }
- int main()
- {
- map_and_call_null();
- }
我们都知道 对于NULL指针的应用是不对的,然而更加深入的探讨NULL指针后你会发现:NULL其实只是一个概念性的东西,也就是说 如果我原因 我可以把任何地址规定为NULL,只要我对其进行处理就可以了。
那么我们会有一个疑问:既然NULL是形式上的概念 ,我们可以利用0地址呢?答案是当然可以。
1)首先,我们通过系统调用mmap 将线性地址的第一个PAGE 加入VMA。注意:必须带用参数MAP_FIXED 否则你系统的
[root@localhost ~]# cat /proc/sys/vm/mmap_min_addr
4096
这个变量值会限制你必须从第二页空闲的地方开始映射。
2)通过1,第一个线性地址page(0-4095),我们已经可以利用,我们写入我们要跳转去执行的函数 指令
addr[0] = '\xff';
addr[1] = '\x25';
*(unsigned int *)&addr[2] = 6;
*(unsigned long *)&addr[6] = (unsigned long)&null_func;
这里有几个地方需要说明一下:
0xff25 这是无条件绝对跳转的opcode
后面紧跟的四个字节:0x00000006 指示了jmp将要跳去的地址在哪。
在这里jmp要跳去的地址存储在0x00000006线性地址中,而0x00000006线性地址存储的正是函数null_func的地址。
3)OK,一切准备就绪。main函数首先调用map_and_call_null(),然后调用aaa函数。此时eip寄存器的值就为0x00000000,CPU去执行0地址处的代码。而0地址处的代码为一绝对跳转指令。从而,去执行了null_func函数。
这就是所谓的hook吧。。。。。
而内核代码是如何处理NULL的呢?
我们知道,缺页时,会触发CPU发出一个PGFAULT缺页异常,而对于缺页异常,由函数do_page_fault进行处理。在将发生缺页异常的线性地址保存在address变量中后,函数转去__do_page_fault 进行处理。
由于存在相应的VMA(mmap函数调用的结果)所以函数不会转去:进行处理。由于我们是读请求造成的缺页,调用handle_mm_fault进行处理,处理完成后,函数返回。
我们的函数也得以继续执行完成,不会被KILL掉。。no segment fault :)
阅读(1855) | 评论(0) | 转发(0) |