▷ 작성자 : minams (minams@a3sc.co.kr)
▷ 편집자 : minams (minams@a3sc.co.kr)
Sys_call_table Wrapping
목차
1. Introduce
2. Find_sys_call_table for kernel 2.6
3. Test kernel 2.6.22
4. Reference
5. Source code
1. Introduce
kernel 개발 등 여러 가지 작업을 하다 보면 부득이하게 Sys_call_table을 수정해야만 하는 상황이 생긴다.
지금 소개하는 방법은 module을 통해 sys_call_table을 wrapping하는 방법으로 kernel 2.6 이상에서도 가능하며 현재, 2.6.22까지 테스트를 마쳤다.
- Module을 사용하는 이유는 kernel을 컴파일 하지 않고도 wrapping 할 수 있어서이다.
2. Find_sys_call_table for kernel 2.6
잘 알려진 방법으로 sys_call_table 보다 주소가 낮은 공개심볼인 loops_per_jiffy와 높은 주소인 boot_cpu_data 를 이용하여 다음과 같은 코드로 sys_call_table 주소를 찾아낼 수 있다.
for ( ptr = ( unsigned long )&loops_per_jiffy;
ptr < ( unsigned long )&init_mm ; ptr += sizeof( void* ) )
{
p = ( unsigned long * )ptr;
if ( p[ 6 ] == ( unsigned long ) sys_close )
{
return (void**)p;
}
} |
그러나 이 소스는 kernel 2.6.22 전까지만 유요한 코드이다. 이유는 심볼 주소가 바뀌었기 때문이다. 확인해보자
Sys_call_table < Loops_per_jiffy < Boot_cpu_data로 변경 된 것을 알 수 있다.
그리하여 a < sys_call_table < b 를 만족하는 공개심볼을 찾아냈다. 바로 _read_lock와 init_mm이다.
# 주소 확인은 직접 해보자!! /boot/System.map 파일을 열어보면 커널 심볼주소(커널 컴파일시)가 들어있다.
이로 만들어지는 코드는 다음과 같다.
for ( ptr = ( unsigned long )_read_lock; ptr < ( unsigned long )&init_mm ; ptr += sizeof( void* ) )
{
p = ( unsigned long * )ptr;
if ( p[ 6 ] == ( unsigned long ) sys_close )
{
return (void**)p;
}
} |
여기서 해결되면 쉽겠지만 문제가 또 하나 발생한다. 이유는 sys_call_table 이 읽기전용으로 바뀐 것이다.
해당 page의 속성을 바꾸기 위해서 다음을 추가하자 해더는 asm/cacheflush.h 이다
// 쓰기로 변경
change_page_attr(virt_to_page(sys_call_table), 1, PAGE_KERNEL);
// 다음으로 캐시라인 전체를 업데이트
global_flush_tlb(); |
3. Test kernel 2.6.22
Sys_open을 다음과 같이 간략히 Wrapping
다음과 같은 실수를 하지 않도록 ㅡㅡㅋ
다음은 컴파일 하는 화면이다.
다은은 Test 하는 화면입니다.
4. 참고 사이트 및 문헌
IT EXPORT 리눅스 커널 프로그래밍
http://monac.egloos.com/1308090
5. Source code
--------minux.c-------------------------------------------------------
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/syscalls.h>
#include <linux/module.h>
#include <asm/unistd.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/ptrace.h>
#include <asm/cacheflush.h>
//extern int change_page_attr;
void **sys_call_table;
asmlinkage int (*org_sys_open)(const char*, int, int);
asmlinkage int (*org_sys_getuid)(void);
asmlinkage int minux_sys_open(char *fname, int flags, int mode)
{
printk( KERN_ALERT "%s file is opened by %d\n",fname,org_sys_getuid() );
return ( org_sys_open( fname, flags, mode ) );
}
static void **find_system_call_table(void)
{
unsigned long ptr;
unsigned long *p;
for( ptr = (unsigned long)_read_lock; ptr < (unsigned long)&init_mm; ptr
+= sizeof(void*) )
{
p = (unsigned long *)ptr;
if( p[6] == (unsigned long)sys_close )
{
return (void**)p;
}
}
return NULL;
}
int __init minux_init(void)
{
sys_call_table = find_system_call_table();
if( sys_call_table != NULL )
{
printk( KERN_ALERT "I think sys_call_table is at 0x%p\n", (void*)
sys_call_table );
//
change_page_attr(virt_to_page(sys_call_table), 1, PAGE_KERNEL);
global_flush_tlb();
//
org_sys_open = sys_call_table[__NR_open];
sys_call_table[__NR_open] = minux_sys_open;
org_sys_getuid = sys_call_table[__NR_getuid];
}
else
{
printk( KERN_ALERT "Failed to find_sys_call_table T.T \n");
}
return 0;
}
void __exit minux_exit(void)
{
sys_call_table[__NR_open] = org_sys_open;
}
module_init(minux_init);
module_exit(minux_exit);
MODULE_LICENSE( "GPL" );
-----------Makefile---------------------------------
KERNELDIR = /lib/modules/$(shell uname -r)/build
obj-m = minux.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -rf *.o
rm -rf *.mod.*
rm -rf .*.cmd
rm -rf *.ko
|