* 다음 사이트의 내용을 정리한 것입니다.
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바이트 형태로 존재한다
- IDA로 ntdll.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;
}