(kernel) Page Protection
Page Protection
x86
#1 set_pages_rw , change_page_attr
1
2
3
4
5
6
7
8
9
10
11
12
13
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
해봐야insmod
시Unknown 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
12
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
7
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
34
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
3
#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
12
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.