Post

(kernel) Page Protection

Page Protection

x86

#1 set_pages_rw , change_page_attr

source/arch/x86/mm/pageattr.c

1
2
3
4
5
6
7
8
9
10
11
12
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를 제거해버리기 때문에 사용할 수 없다.
  • EXPORT_SYMBOL()로 지정되어 있지 않아서 사용할 수 없다.
  • source/arch/x86/include/asm/set_memory.h 에 정의되어 있기는 하지만 이를 include해봐야 insmodUnknown symbol in module 에러가 발생한다.
  • kenrel 2.6.24 이하 버전에서는 change\_page_attr()이 직접 export 되어 있으나, 버전이 올라가면서 함수 자체가 사라졌다.
#2 lookup_address

직접 page table entry를 찾아내 page에 W 권한을 주는 방법. 단일 page의 속성만 변경하기 때문에 안정적이다. /source/arch/x86/mm/pageattr.c#L406 : lookup_address 다음과 같이 직접 정의해서 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
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를 수정해 쓰기가 가능하도록 하는 방법. 일단 변경하면 모든 페이지에 대해 쓰기가 가능해진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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

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

1
2
#define cr0\_WP\_off    write\_cr0( read\_cr0() & (~0x10000) );
#define cr0\_WP\_on     write\_cr0( read\_cr0() | 0x10000 );

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

쉘코드에 넣어야 하는 경우

1
2
3
4
5
6
7
8
9
10
11
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

/source/arch/arm/include/asm/set_memory.h

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

This post is licensed under CC BY 4.0 by the author.