Header

  1. View current page

    Arma

09) Technical document

 

커널 패치 제작하기 #

 diff 명령어를 사용하여 패치를 제작하는데, 주의할 점은 일반적으로 patch -p1로 사용한다고 가정을 하고, 리눅스 경로까지 포함하여 패치 경로를 설정해주어야 한다는 점이다.

# diff -urN linux-2.6.24.4.orig linux-2.6.24.4.self32 > self32.diff

제작된 패치 파일을 바닐라 커널(linux-2.6.24.4 버전)에 적용하기 위해선, 커널 압축을 해제한 상태에서 해당 디렉토리에 self32.diff 파일을 복사한 후 아래와 같이 입력하면 된다.

linux-2.6.24.4.orig$ patch -p1 < self32.diff
patching file arch/x86/kernel/Makefile_32
patching file arch/x86/kernel/cpu/common.c
patching file arch/x86/kernel/entry_32.S
patching file arch/x86/kernel/secure_layer.c
patching file arch/x86/kernel/secure_layer.h
patching file arch/x86/kernel/traps_32.c
patching file include/asm-x86/desc_32.h
patching file include/asm-x86/segment_32.h
patching file security/Kconfig

위와 같이 간단히 적용되는 것을 확인 할 수 있다.

 

커널 메뉴에 추가하기#

 리눅스 커널이 있는 공간에 각 폴더에 Kconfig라는 파일이 있을 것이다. 이 파일을 잘 수정하면, make menuconfig 할 때 나오는 아래 화면에 추가할 수 있다.

 

 screen01.png

 

 먼저 arch/x86/Kconfig의 내용은 아래와 같다.

 # x86 configuration
mainmenu "Linux Kernel Configuration for x86"

# Select 32 or 64 bit
config 64BIT
    bool "64-bit kernel" if ARCH = "x86"
    default ARCH = "x86_64"
    help
      Say yes to build a 64-bit kernel - formerly known as x86_64
      Say no to build a 32-bit kernel - formerly known as i386

config X86_32
    def_bool !64BIT

config X86_64
    def_bool 64BIT

### Arch settings
config X86
    def_bool y
    select HAVE_IDE
    select HAVE_OPROFILE
    select HAVE_KPROBES
    select HAVE_KRETPROBES
    select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)

config ARCH_DEFCONFIG
    string
    default "arch/x86/configs/i386_defconfig" if X86_32
    default "arch/x86/configs/x86_64_defconfig" if X86_64

 뭐 문법을 특히 배우지 않아도 금방 알아 차릴 것이다.(나는 실제로 make menuconfig 화면을 띄운 상태에서 각 디렉토리 마다 있는 Kconfig 파일을 살펴보는 방식으로 금방 익혀버렸다)

그냥 그대로 적용해버렸다. security 디렉토리가 적당한 듯하여, security/Kconfig 파일 마지막 endmenu 위에 아래와 같은 내용을 추가하였다.

config SECURE_LAYER
        bool "Secure Layer Support for SELF32"
        depends on X86_32
        default n
        help
          This enables SELF32 project protecting the ELF32.
          It prevents any programs analyzed with dis-assembling and
          debugging.

          See <http://self32.springnote.com> for more information
          about this module.

          If you are unsure how to answer this qeustion, answer N.

config DEBUG_SECURE_LAYER
        bool "Debug Secure Layer"# if SECURE_LAYER
        depends on SECURE_LAYER
        default n
        help
                Enable this to display kernel-message from SELF32.

 

endmenu

 아주 간단하다. 그리고 make menuconfig로 확인해보면 아래 그림과 같이 생길 것이다.

screen02.png 

 도움말 정보(?)는 위에서 help 란에 입력한 것과 같이 나올 것이다.

screen03.png 

screen04.png

하지만, 아직 끝난 것이 아니다. 바로 소스 상에서 위 옵션과 연결을 해야하기 때문이다.

 .h나 .c, .S 등에선 #ifdef CONFIG_SECURE_LAYER와 같이 Kconfig에서 사용한 config 이름에 접두어 CONFIG_만 붙이면 그대로 사용이 가능하다.

  1. #ifdef CONFIG_SECURE_LAYER
  2. ENTRY(secure_layer)
  3. .....
  4. ENDPROC(secure_layer)
  5. #endif

 또한, Makefile에도 적용이 가능하다.

  1. obj-$(CONFIG_SECURE_LAYER) += secure_layer.o

 위와 같은 라인을 추가하면 Kconfig 옵션에 따라서 컴파일 여부를 결정할 수 있다.

 

Blowfish API 사용하기#

  1.  int main(int argc, char** argv)
    {
     BF_KEY key;
     const unsigned char data[64] = { '\0', };
     unsigned char in[256] = { '\0', };
     unsigned char out[256] = { '\0', };
     unsigned char text[256] = { '\0', };
  2.  unsigned char ivec[8] = { '\0', };
  3.  strcpy(in, "this is a very important documents");
     
     BF_set_key(&key, 64, data);
  4.  BF_cbc_encrypt(in, out, strlen(in), &key, ivec, BF_ENCRYPT);
  5.  memset(ivec, '\0', 8);
     BF_cbc_encrypt(out, text, strlen(out), &key, ivec, BF_DECRYPT);
     
     printf("%s\n", text);
  6.  return 0;
    }

openSSL 에 존재하는 Blowfish 암호화 방법이다. BF_cbc_encrypt 라는 함수를 사용해서 암호화와 복호화가 가능. 여기서 ivec을 중간에 memset 을 해주는데(memset(ivec, '\0', 8);) 이렇게 하지 않으면 복호화 되는 문자열이 깨진다. 즉 NULL 이 존재해야 한다.

  1. gcc -g -o main main.c -I/usr/local/include -L/usr/local/lib -lcrypto

 

커널 컴파일하기 #

커널을 컴파일하는 방법은 아주 많다. 2.6.x 버전에 와서는 make && make modules && make modules_install && make install 만 하면 바로 된다. 하지만 예외적인 시스템도 물론 있는 법. 이전 서버 환경인 한컴 리눅스 2005에선 위와 같이 해도 잘 되었지만, 이번 서버 환경인 우분투 7.04에선 잘 되지 않는다. 물론, 우분투 패키징 시스템을 사용하면 되지만, deb 파일을 생성해야 된다는 단점이 있어서, 어떻게든 해보려고 노력하고 잘 된 자료를 이 곳에 기록한다.

$ sudo make

$ sudo make modules

$ sudo make modules_install

$ sudo make install    <= 이상하게 여기서 initrd.img를 생성해주지 못한다.

$ sudo mkinitramfs -o /boot/initrd.img-<커널버전> <커널버전>

$ [/boot/grub/menu.lst를 직접 수정해야 함]

 modules에 대해서는 새로이 모듈을 수정하지 않았다면, 할 필요가 없다. -- 2008/08/04 00:40:07 조성현

 

디스어셈블리 라이브러리 사용 #

 참고 페이지(http://en.wikibooks.org/wiki/X86_Disassembly/Disassemblers_and_Decompilers)에 내용이 있으며, 간단한 사용방법은 sample_code에 있다.

 샘플을 참고하여 만든 일정한 범위 내에 기계어를 어셈블리어 아스키 코드로 바꾸어주는 함수이다.

  1. void print_asm_ascii(x86_insn_t *insn, unsigned int *arg[2])
    {
        char line[256];
        char buf[256];
        char **asm_ascii = (char **)arg[0];
  2.     x86_format_insn(insn, line, 256, att_syntax);
        sprintf(buf, "%s\n", line);
        arg[1] += (int)strlen(buf);
        *asm_ascii = (char *)realloc(*asm_ascii, (size_t)arg[1]);
        strcat(*asm_ascii, buf);
    }
    int print_asm(char **asm_ascii, unsigned char *hex_bin, int bin_length)
    {
        unsigned int arg[2];
  3.     arg[0] = (unsigned int)asm_ascii;
        arg[1] = 0;
  4.     // initialize memory
        *asm_ascii = (char *)calloc(256, sizeof(char));
  5.     x86_init(opt_att_mnemonics, 0, 0);
        x86_disasm_range(hex_bin, 0, 0, bin_length,
            (void (*)(x86_insn_t *, void *arg))print_asm_ascii,(unsigned int *)arg);
        x86_cleanup();
  6.     return (int)arg[1];
    }

 먼저 print_asm_ascii 함수는 내부적으로 콜백 정의가 되는 함수로, print_asm 함수에서 x86_disasm_range() 함수로 연결한다. 이 x86_disasm_range() 함수는 일정한 범위의 기계어를 읽어들여, 한 명령어가 읽혀질 때마다 정의된 콜백 함수로 제어권을 넘겨주는 역할을 한다. 간단히, 위와 같이 연결한 후, print_asm_ascii 함수에서 x86_insn_t 구조체의 포인터를 가지고 있는 *insn에 대한 처리를 하면 된다. 이 x86_insn_t 구조체는 한 명령에 대한 정보를 저장하고 있으며, 이 명령어를 어셈블리어 명령어로 바꿀 수 있는 데, 바로 x86_format_insn() 함수를 이용하면 된다. 위 코드를 살펴보면, 이해하기 쉬울 것이다. -- 2008/08/04 00:33:04 조성현

 

커널 디버깅#

커널에 심은 프로그램이 제대로 동작하는지 확인하기 위해 디버깅이 필요하다. printk로 어떻게 확인은 가능하겠지만, 에러가 발생한다면 디버깅 수준의 작업이 필요하게 된다. 일단 디버깅을 시도할 수 있는 방법으로 아래와 같이 정리한다.

 

 

인라인 어셈블리 프로그래밍#

어셈블리어를 C언어 중간에 포함할 수 있도록 C 컴파일러가 제공하는 것을 인라인 어셈블리라고 한다. 관련 자료는 아래에 가면 자세하게 나와 있으며, 혹시나 해서 샘플 소스를 올려 본다.

 

malloc()으로 할당한 메모리 공간에 원하는 코드를 심는 프로그램이다. 간단하지만, 인라인 어셈블리를 사용하기엔 이 정도만 봐도 될 것으로 보인다.

  1. #include <stdio.h>
    #include <stdlib.h>
  2. int main(int argc, char *argv[])
    {
        int i;
        int *addr;
  3.     char *buf;
  4.     buf = malloc(100);
  5.     printf("pre-_-\n");
        for (i = 0; i < 100; i++)
        {
            printf("%02X ", (char)buf[i]);
            if (i % 16 == 15) printf("\n");
        }
        printf("\n");
  6.     __asm__ __volatile__(
            "pusha;"
            "leal test_,%%eax;"
            "movl %%eax,%0;"
            "push %%es;"
            "push %%ds;"
            : "=m" (addr));
        printf("addr in previous = %8p\n", addr);
  7.     __asm__ __volatile__(
            "pop %%es;"     // es <= ds
    /*  address of *buf
            "leal %2,%%eax;"
            "movl (%%eax),%%ebx;"
            "movl %%ebx,%0;"
    */
  8.         "leal test_,%%esi;"
            "leal %1,%%eax;"
            "movl (%%eax),%%edi;"
            "movl $(_end-test_),%%ecx;"
            "rep;"
            "movsb;"
  9.         "jmp _end;"
            "test_:"
            "movl $4,%%eax;"
            "movl $1,%%ebx;"
            "leal str_hello,%%ecx;"
            "movl $14,%%edx;"
            "int $0x80;"
            "ret;"
            "str_hello:"
            ".string \"Hello World!\\n\";"
            "_end:"
            "pop %%es;"
            "popa;"
            "call test_\n\t"
            "call *%0\n\t"
            : "=m" (addr)
  10.         : "m" (buf));
  11.     printf("addr = %8p\n", addr);
  12.     printf("post-_-\n");
        for (i = 0; i < 100; i++)
        {
            printf("%02X ", (unsigned char)buf[i]);
            if (i % 16 == 15) printf("\n");
        }
        printf("\n");
  13.     free(buf);
  14.     return 0;
    }

 이 파일을 컴파일하여 실행한 모습으로 아래와 같다.

 $ ./test4
pre-_-
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00
addr in previous = 0x80484b9
Hello World!
Hello World!
addr = 0x80484b9
post-_-
B8 04 00 00 00 BB 01 00 00 00 8D 0D D1 84 04 08
BA 0E 00 00 00 CD 80 C3 48 65 6C 6C 6F 20 57 6F
72 6C 64 21 0A 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00

 00으로 표시되는 값은 malloc()으로 할당된 메모리 값을 화면에 뿌려준 것이고, 아래 코드를 보면 B8 04 .... 등으로 쓰여진 것을 확인할 수 있다.

작성 정보#

  • inline assembly 추가 -- 2008/05/31 02:06:49 조성현
  • 페이지 생성 및 커널 디버깅 정보 기록 -- 2008/05/29 21:30:36 조성현

 

History

Last edited on 09/04/2008 04:41 by wbhacker

Comments (0)

You must log in to leave a comment. Please sign in.