Sys_call_table Wrapping

(무선)네트워크 2009. 1. 5. 14:45 Posted by 알 수 없는 사용자


▷ 작성자 : 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