Page Protection

x86

#1 set_pages_rw , change_page_attr

```c

int set_pages_rw(struct page *page, int numpages)  // use virt_to_page()

{

unsigned long addr = (unsigned long)page_address(page);

return set_memory_rw(addr, numpages);

}

int set_memory_rw(unsigned long addr, int numpages)

{

return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_RW), 0);

}

```

  • 특정 영역(BIOS, .rodata, ...)이 들어오면 `` _PAGE_RW`` flag를 제거해버리기 때문에 사용할 수 없다.
  • ``c EXPORT_SYMBOL()``로 지정되어 있지 않아서 사용할 수 없다.
  • source/arch/x86/include/asm/set_memory.h에 정의되어 있기는 하지만 이를 ``c include``해봐야 `` insmod`` 시 `` Unknown symbol in module`` 에러가 발생한다.
  • kenrel 2.6.24 이하 버전에서는 ``c change_page_attr()``이 직접 export 되어 있으나, 버전이 올라가면서 함수 자체가 사라졌다.


#2 lookup_address

직접 page table entry를 찾아내 page에 W 권한을 주는 방법.
단일 page의 속성만 변경하기 때문에 안정적이다.
다음과 같이 직접 정의해서 사용하면 된다.
```c
static void set_page_rw(unsigned long addr, int bit){
    unsigned int level;
    pte_t *pte;

    pte = lookup_address(addr, &level);
    if (bit == 1) {
        pte->pte |= _PAGE_RW;
    }
    else {
        pte->pte &= ~_PAGE_RW;
    }
}
```

다음을 확인해 보면 physical address를 찾아가는 페이징 과정을 그대로 따라간다는 것을 알 수 있다.

/source/arch/x86/mm/pageattr.c#L353 : lookup_addersss_in_pgd

```c

virt_addr 

→ pgd ( Page Global Directory )

→ pud ( Page Upper  Directory )

→ pmd ( Page Middle Directory )

→ pte ( Page Table Entry )  // return *pte_t

Page Frame ( phys_addr )

```


#3 cr0

원래 kernel mode에서는 page의 `` rwx``를 무시하고 쓰고, 실행할 수 있으나 WP가 설정되어 있는 경우 쓰기가 제한되므로, cr0 레지스터의 WP bit를 수정해 쓰기가 가능하도록 하는 방법.
일단 변경하면 모든 페이지에 대해 쓰기가 가능해진다.
```c

0 PE Protected Mode Enable   1 : protected mode, 0 : real mode

1 MP Monitor co-processor

2 EM Emulation

3 TS Task switched

4 ET Extension type

5 NE Numeric error

16 WP Write protect     the CPU can't write to read-only pages when privilege level is 0

18 AM Alignment mask

29 NW Not-write through

30 CD Cache disable

31 PG Paging

```


/source/arch/x86/include/asm/special_insns.h#L142 : read_cr0 / write_cr0

매크로로 등록해서 사용하면 된다.

```c

#define cr0_WP_off    write_cr0( read_cr0() & (~0x10000) );

#define cr0_WP_on     write_cr0( read_cr0() | 0x10000 );

```

만약 사용할 수 없는 경우, 구현이 간단하므로 직접 인라인 어셈블리 작성해서 사용하면 된다.


쉘코드에 넣어야 하는 경우

```c

asm volatile(

    "pushl %eax             \n\t"

    "pushl %ebx             \n\t"

    "movl %cr0, %eax        \n\t"

    "movl $0x10000, %ebx    \n\t"

    "notl %ebx              \n\t"

    "andl %ebx, %eax        \n\t"

    "movl %eax, %cr0        \n\t"

    "popl %ebx              \n\t"

    "popl %eax"

);

```


arm

set_memory_rw

.h 정의는 되어 있으나, `` CONFIG_MMU`` flag가 없는 경우 무조건 0을 리턴하도록 되어 있다.


'OS > Kernel' 카테고리의 다른 글

[kernel] hook sys_call_table  (0) 2017.10.20
[kernel] get sys_call_table  (0) 2017.10.16
[kernel] addr_limit : kernel space arg  (0) 2017.10.15
[kernel] LKM, Loadable Kernel Module / Kernel Compile  (0) 2017.10.14
[kernel] virt_to_phys  (0) 2017.10.13