1. 程式人生 > >C語言花式玩法之把函數拷貝到數組執行 (需要mprotect)

C語言花式玩法之把函數拷貝到數組執行 (需要mprotect)

return 內容 報錯 rwx UNC error errno.h 價值 沒有

在閱讀內核代碼的時候,明白了內核是通過頁表項中的標誌位_PAGE_READ,_PAGE_WRITE,_PAGE_EXECUTE來區分頁的權限的。

進程在內核中的地址空間代碼段,數據段,堆,棧之間最大的區別也是權限的區別,而系統調用mprotect恰好是用來改變內存頁的權限的。

是否可以通過mprotect可以把棧上的數組修改成可執行權限,然後把函數內容拷貝到數組執行呢?值得嘗試一下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/mman.h>
 4 #include <sys/types.h>
 5
#include <string.h> 6 #include <errno.h> 7 #include <unistd.h> 8 9 int m_add(int i) 10 { 11 return i+10; 12 } 13 14 int show_map() 15 { 16 char command[256]; 17 sprintf(command, "cat /proc/%d/maps", getpid()); 18 system(command); 19 } 20 21 int main()
22 { 23 int (*func_p)(int); 24 int size; 25 int ret; 26 char m[1000]; 27 size = (char *)show_map - (char *)m_add; 28 printf("size = %d\n",size); 29 func_p = m; 30 31 ret = mprotect((void *)((long)func_p&(~0XFFFUL)), 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
32 if(ret < 0){ 33 perror("mprotect: "); 34 exit(ret); 35 } 36 37 memcpy(func_p,m_add,size); 38 39 printf("m_add(24) = %d\n",m_add(24)); 40 printf("func_p(63) = %d\n",func_p(63)); 41 printf("m_add = %p, func_p = %p\n", m_add, func_p); 42 show_map(); 43 return 0; 44 }

執行結果:

size = 15
m_add(24) = 34
func_p(63) = 73
m_add = 0x561528db08c0, func_p = 0x7ffd4c19e300
561528db0000-561528db1000 r-xp 00000000 fe:01 477434                     /home/test/a.out
561528fb0000-561528fb1000 r--p 00000000 fe:01 477434                     /home/test/a.out
561528fb1000-561528fb2000 rw-p 00001000 fe:01 477434                     /home/test/a.out
5615298e7000-561529908000 rw-p 00000000 00:00 0                          [heap]
7fcc3280c000-7fcc329a1000 r-xp 00000000 fe:01 329984                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcc329a1000-7fcc32ba1000 ---p 00195000 fe:01 329984                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcc32ba1000-7fcc32ba5000 r--p 00195000 fe:01 329984                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcc32ba5000-7fcc32ba7000 rw-p 00199000 fe:01 329984                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcc32ba7000-7fcc32bab000 rw-p 00000000 00:00 0
7fcc32bab000-7fcc32bce000 r-xp 00000000 fe:01 329980                     /lib/x86_64-linux-gnu/ld-2.24.so
7fcc32dbc000-7fcc32dbe000 rw-p 00000000 00:00 0
7fcc32dcb000-7fcc32dce000 rw-p 00000000 00:00 0
7fcc32dce000-7fcc32dcf000 r--p 00023000 fe:01 329980                     /lib/x86_64-linux-gnu/ld-2.24.so
7fcc32dcf000-7fcc32dd0000 rw-p 00024000 fe:01 329980                     /lib/x86_64-linux-gnu/ld-2.24.so
7fcc32dd0000-7fcc32dd1000 rw-p 00000000 00:00 0
7ffd4c17e000-7ffd4c19e000 rw-p 00000000 00:00 0
7ffd4c19e000-7ffd4c19f000 rwxp 00000000 00:00 0                          [stack]
7ffd4c1e1000-7ffd4c1e3000 r--p 00000000 00:00 0                          [vvar]
7ffd4c1e3000-7ffd4c1e5000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

可以看到,函數指針func_p實際指向的是char數組m[1000]的地址,其值為0x7ffd4c19e300 確實位於棧7ffd4c19e000-7ffd4c19f000上面,而其成功的執行了+10的操作,返回了正確結果。

這個例子沒有實際應用價值,一旦函數中存在庫函數的調用,一般就會報錯了。

不過它說明了從C語言往下看,棧,堆,數據段,代碼段統統都是內存,只要配置相同,相互之間並無本質區別。

C語言是操作內存的語言,在經過操作系統的封裝後,它所看到的是無差別的內存。所有的數據類型,你都可以把它們理解成語法糖。

那它和匯編有什麽區別?匯編是操作CPU和寄存器的語言,可以訪問CPU專有指令,可以訪問特定的寄存器,而C語言的初衷,便是抹平體系結構之間的差異。

C語言花式玩法之把函數拷貝到數組執行 (需要mprotect)