Linux mprotect函數 -- control the protection of pages

  • 函數原型

    int mprotect(void *addr, size_t len, int prot);
    
  • 頭文件

    #include <sys/mman.h>
    #include <unistd.h>
    
  • 描述

    control the protection of pages.

    The mprotect() system call changes the specified pages to have protection prot. When a program violates the protections of a page, it gets a SIGBUS or SIGSEGV signal.

    主要是用來修改一段指定內存區域的保護屬性。

    使用mprotect這裏最重要的一點是被保護的內存是按頁對齊的,範圍也是按頁來的。這是因爲Linux管理進程地址空間是一VMA(Virtual Memory Area)爲單位來管理進程虛擬地址空間的,而VMA必須是page size大小的整數倍。

  • 參數

     PROT_NONE   No permissions at all.
     PROT_READ   The pages can be read.
     PROT_WRITE  The pages can be written.
     PROT_EXEC   The pages can be executed.
    
  • 返回值

    Upon successful completion, a value of 0 is returned. Otherwise, a value of -1 is returned and errno is set to indicate the error.

    常見的錯誤有:

    • EACCES

      該內存不能設置爲相應權限。這是可能發生的,比如,如果你 mmap(2) 映射一個文件爲只讀的,接着使用 mprotect() 標誌爲 PROT_WRITE。

    • EINVAL

      start 不是一個有效的指針,指向的不是某個內存頁的開頭。

    • ENOMEM

      內核內部的結構體無法分配。

  • 舉個例子,修改虛函數表

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/mman.h>
    
    void print()
    {
    	printf("print\n");
    }
    
    class Base 
    {
    public:
    	virtual void print(){
    		printf("Base::print\n");
    	} 
    };
    
    void changeVtable()
    {
    	Base base;
    	void** vtable= *(void***)&base;
    	// 獲取頁大小
    	unsigned long pagesize = sysconf(_SC_PAGE_SIZE);
    	// 內存按頁對齊
    	void* align_page_addr = reinterpret_cast<void*>(reinterpret_cast<unsigned long>(&vtable[0]) & (~(pagesize-1)));
    	// 修改權限
    	int ret = mprotect(align_page_addr, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
    	if(ret != 0) {
    		printf("mprotect error\n");
    		return;
    	}
    	// 修改虛函數表內容
    	vtable[0] = reinterpret_cast<void*>(print);
    }
    
    int main()
    {
    
    	Base* pbase = new Base();
    	Base base;
    	printf("%p\n",print);
    	printf("%p\n",reinterpret_cast<void*>(**(void***)(&base)));
    	printf("%p\n",reinterpret_cast<void*>(**(void***)(pbase)));
    
    	print();
    	base.print();
    	pbase->print();
    
    	changeVtable();
    
    	printf("%p\n",print);
    	printf("%p\n",reinterpret_cast<void*>(**(void***)(&base)));
    	printf("%p\n",reinterpret_cast<void*>(**(void***)(pbase)));
    
    	print();
    	base.print();
    	pbase->print();
    
    	delete pbase;
    	return 0;
    }
    

    輸出結果爲:

    0x10f7f1cd0
    0x10f7f1ee0
    0x10f7f1ee0
    print
    Base::print
    Base::print
    0x10f7f1cd0
    0x10f7f1cd0
    0x10f7f1cd0
    print
    Base::print
    print
    

    注意虛函數的調用時刻。

發佈了55 篇原創文章 · 獲贊 3 · 訪問量 6610
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章