My Own Style

반응형
  • 후킹이란?
    각종 프로그램에서 소프트웨어 구성 요소 간에 발생하는 함수 호출, 메시지, 이벤트 등을 중간에 바꾸거나
    가로채는 방법을 말함. 보통 기존 함수에 대한 호출을 중간에 가로채어 런타임에 해당 함수의 동작을 수정하기 위해 많이 사용됨.

  • 공유라이브러리 후킹의 원리
    공유 라이브러리 후킹의 원리를 알기위해서는 간단하게라도 소스코드를 컴파일하여 프로그램을 만들고 해당 프로그램을 실행시켰을 때 어떤 동작이 수행되는지에 대해 알고 있어야함.
    • 프로그램 컴파일 내용 및 실행 내용 요약 
      • 공유라이브러리를 사용할 소스코드를 컴파일 하게되면 최종적으로 링커카 라이브러리의 symbol 을 링크하여 최종 프로그램이 생성됨.
        • 링커가  /etc/ld.so.conf 에 지정된 디렉토리를 찾아다니면서 심볼릭 링크를 만들어 연결됨. 
      • 공유 라이브러리와 연결된 프로그램을 실행하게되면 내부적으로 dynamic loader 가 먼저 동작함.
        (linux 의 경우 로더의 이름은 ld.so or ld-linux.so)

      • dynamic loader 는 연결된 라이브러리를 메모리에 로딩시킴
        • loader 가 /lib/ld.so.cache 를 바라보고 실행 파일에서 필요로하는 라이브러리를 찾아 메모리에 로딩시켜 놓음.

      • main 함수를 찾아서 실행시킴.

이렇듯 프로그램 실행시, 로더는 어떤 공유라이브러리를 참조할지 미리 메모리에 로딩시켜 놓는 작업을 하는데 LD_PRELOAD 환경 변수는 로더가 먼저 로드할 공유 라이브러리를 지정하는데 사용됨.
예를들어 libc 함수명과 동일한 함수명을 가진 test 라는 공유라이브러리를 LD_PRELOAD 환경 변수를 이용하여 지정해 놓는다면 test 라이브러리가 먼저 로딩되어 있기 때문에 동일한 함수명을 test 라이브러리에서 먼저 찾아 동작하게 됨.

  • 예시
    • close 함수와 socket 함수를 hooking 한 예
      (close 함수와 socket 함수를 호출될 때, 호출한 함수와 fd 값을 출력하도록 작성 - hooking.c)
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
// 표준함수 close 와 동일한 함수 정의
int close(int fildes)
{
    int (*new_close)(int fildes);
    int result = 0;
 
    // close 함수의 정상적인 동작을 위해  Hooking 함수에서 표준 close 함수 호출
    new_close = dlsym(RTLD_NEXT, "close");
    result = new_close(fildes);
 
    // FD값이 100 이상인 경우에 한하여 close 호출시 fd 값과, Backtrace 값을 찍도록함
    if(fildes > 100)
    {
        printf("\e[1;31m [test]close(%d) ret(%d)\e[0m\n", fildes, result);
        int j, nptrs;
#define BTSIZE (3)
        void *trace[100];
        char **strings;
        memset(trace, 0x00, 100);
        nptrs = backtrace(trace, BTSIZE);
        printf("backtrace() returned %d addresses\n", nptrs);
        if(nptrs > 0)
        {
            strings = backtrace_symbols(trace, nptrs);
            if (strings != NULL)
            {
                for (j = 1; j < nptrs; j++)
                {
                    printf("%s\n", strings[j]);
                }
                free(strings);
            }
        }
    }  
 
    return result;
}
 
 
int socket(int domain, int type, int protocol)
{
    int (*new_socket)(int , int , int);
    int result = 0;
    new_socket= dlsym(RTLD_NEXT, "socket");
    result = new_socket(domain, type, protocol);
    {
        printf("\e[1;31m[test] socket(%d, %d, %d) ret(%d)\e[0m\n", domain, type, protocol, result);
        int j, nptrs;
#define BTSIZE (3)
        void *trace[100];
        char **strings;
        memset(trace, 0x00, 100);
        nptrs = backtrace(trace, BTSIZE);
        printf("backtrace() returned %d addresses\n", nptrs);
        if(nptrs > 0)
        {
            strings = backtrace_symbols(trace, nptrs);
            if (strings != NULL)
            {
                for (j = 1; j < nptrs; j++)
                {
                    printf("%s\n", strings[j]);
                }
                free(strings);
            }
        }
    }
 
    return result;
}


- 위의 코드에서 dlsym() 용도

ex)
static void* (*my_malloc)(size_t) = NULL;
my_malloc = dlsym(RTLD_NEXT, "malloc");


이라면 lib 에서 처음 찾은 malloc 함수의 주소가 아니라
두번째로 찾은 malloc 의 함수 주소를 받아오는 내용

  • hooking library 생성
$ gcc hooking.c -o hooking.so -fPIC -shared -ldl -D_GNU_SOURCE
  • hooking library 사용하는 application 빌드 (test.c)
$ gcc test.c -o test -rdynamic
  • application hooking libary 를 먼저 load 할 수 있도록 환경 변수 설정
$ export LD_PRELOAD="hooking.so"


- 참고 페이지 : https://blog.netspi.com/function-hooking-part-i-hooking-shared-library-function-calls-in-linux/

반응형

이 글을 공유합시다

facebook twitter googleplus kakaoTalk kakaostory naver band