본문 바로가기

프로그래밍

SSDT Hook

 [windows inside] SSDT Hook 과정- 정리

* 다음 사이트의 내용을 정리한 것입니다.

http://beist.org/research/research1.html

http://somma.egloos.com/2731001

 

//=======================================================================================

// 참고

- SSDT(System Service Descriptor Table) = KeServiceDescriptorTable:: KiServiceTable

- Native API = System Service (Dispatcher)

 

* DKOM= Direct Kernel Object Manipulation

   Object Manager를 거치지 않고 커널 오브젝트를 직접적으로 수정하게 하는 커널 해킹 기법

 

* int 0x2E 명령 :  이 코드는 유저 모드에서 커널 모드로의 제어 이행을 수행하는 코드

 소프트웨어 인터럽트를 발생시키고, 유저 스택을 커널 스택으로 변경

 int 0x2E의 인터럽트 핸들러인 KiSystemService()를 실행

   Windows 2000까지만 커널로의 이행을 수행하는 게이트

 

* SYSENTER - 펜티엄2이상의 사양을 갖춘 XP 이후에서부터는 int 2e 대신 SYSENTER를 사용

 동일한 기능을 수행하지만 SYSENTER는 커널로의 이행을 좀더 빠르게 수행

 KiFastCallEntry() 실행

//=======================================================================================

 

* SSDT Hook 순서

   - OpenProcess() Hook

 

1. ZwOpenProcess 주소값 알아내기

 

 * KeServiceDescriptorTable 의 주소를 알아내기

   __declspec(dllimport)  SERVICE_DESCRIPTOR_ENTRY KeServiceDescriptorTable;// direct import KeServiceDescriptorTable

 

* KeServiceDescriptorTable 을 위한 SERVICE_DESCRIPTOR_ENTRY 구조체.

   - 사용자가 프로그램내에서 수동으로 정의해 줘야 함

typedef struct _SERVICE_DESCRIPTOR_ENTRY

{

   unsigned int *ServiceTableBase; //SSDT(KiServiceTable)의 주소, 각 서비스 별 매핑 함수 포인터들의 목록이 저장된 테이블의 포인터,

   unsigned int *ServiceCounterTableBase; //무시

   unsigned int NumberOfServices; //Native API의 총 개수, (Service=Native API)

   unsigned char *ParamTableBase; //KiArgumentTable의 주소 값, 각 서비스별 파라미터의 크기를 나타내는 값을 가진 테이블의 포인터,

} SERVICE_DESCRIPTOR_ENTRY, *PSERVICE_DESCRIPTOR_ENTRY;

 

* System call 의 서비스 인덱스 번호 알아내기

   - system call 의 두 번째 바이트에 4바이트 형태로 존재한다

   - IDAntdll.dll를 디스어셈블 한후 목표함수로 이동해서 어셈소스의 첫줄, 또는 Windbg 사용

lkd> u nt!zwopenprocess

nt!ZwOpenProcess:

804e5c0a b87a000000      mov     eax,7Ah // 바로요기, 0x7a = System call 의 서비스 인덱스 번호

804e5c0f 8d542404        lea     edx,[esp+4]

804e5c13 9c              pushfd

804e5c14 6a08            push    8

804e5c16 e8b69bffff      call    nt!KiSystemService (804df7d1)

804e5c1b c21000          ret     10h

nt!ZwOpenProcessToken:

804e5c1e b87b000000      mov     eax,7Bh

804e5c23 8d542404        lea     edx,[esp+4]

 

- ZwOpenProcess함수 주소   =  KeServiceDescriptorTable.ServiceBase[0x7A]

 

// native api function address get macro

#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function + 1)

#define ORG_SYSCALL_PTR(_orgFunc) \

&(((PLONG) KeServiceDescriptorTable.ServiceTableBase)[SYSCALL_INDEX(_orgFunc)])

 

 

//=========================================================================================================

2. Write Protection 해제

   - SSDT[System Service Discription Table]=(KiServiceTable) 변경을 위해 커널메모리 쓰기가 가능해야 함

   - CR0(Control Register 0) WP(Write Protection) 비트를 0으로 만든다.

   - 다른 방법 : MDL (Memory Descriptor List) 를 이용

 

// 

#define   CR0_WP_MASK     0x0FFFEFFFF

 

VOID  ClearWriteProtect(VOID)

{

  __asm

  {

    push  eax;

    mov   eax, cr0;

    and   eax, CR0_WP_MASK; // WP 클리어

    mov   cr0, eax;

    pop   eax;

  }

}

 

VOID  SetWriteProtect(VOID)

{

  __asm

  {

    push  eax;

    mov   eax, cr0;

    or    eax, not CR0_WP_MASK; // WP 비트 세팅

    mov   cr0, eax;

    pop   eax;

  }

}

 

 

//=========================================================================================================

3. ZwOpenProcess 를 대체할 hook procedure 작성

 

// new function

NTSTATUS newZwOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL   )

{

   //무조건 실패 리턴

   NTSTATUS status = status = STATUS_INVALID_CID;    // An invalid Client ID was specified.

   return status;

}