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 |

Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다