Programming/Kernel / Driver2008. 12. 5. 13:11

작업 관리자를 보면 현재 실행되고 있는 프로세스들이 있습니다.

이 프로세스들은 아주 간단한 손터치로 인해 강제 종료를 손쉽게 할 수 있습니다.

 

 

자 그럼 작업 관리자를 통해서 프로세스 강제 종료 보호하는 방법은 어떤 것 들이 있을까요?

 

제가 설명드리고자 하는 방법은 ZwOpenProcess()함수를 후킹하는 방법에 소개를 하겠습니다.

(SSDT 후킹 방법은 제 블로그에 올린 강좌나 다른 인터넷 자료를 참고하세요...^^;)

 

ZwOpenProcess()함수를 후킹하여 해당 함수의 리턴값을 "STATUS_INVALID_CID"로

리턴하면 됩니다.

 

아주 쉽죠? 이렇게 하면 다음 화면처럼 해당 프로세스를 강제 종료하지 못합니다.

 

 

참고) ZwOpenProcess()함수를 후킹하면 나쁜 목적으로 해당 프로세스에 접근하여 메모리를

        참조하는 것 또한  방지가 됩니다.

Posted by skensita
Programming/Kernel / Driver2008. 12. 3. 11:05

특정 프로세스를 보호하는 데에는 여러 가지 방법이 존재합니다.

일반적으로 하는 SDT Hook 이외에 여러 방법들이 존재하는데, 지금부터 그 방법들을 알아봅시다.


1. ZwXxx() Hook

일반적으로 많이 쓰이는 방법이라 생각하는데, 대부분 이러한 함수들을 후킹함으로써 프로세스를 보호합니다.

ZwOpenProcess() : 프로세스 핸들을 얻어오지 못하게 해서 프로세스에 접근하지 못하게 합니다.

ZwTerminateProcess() : 프로세스 핸들을 얻지 못하게 했다 해도, 프로세스 핸들을 얻어오는 또다른 방법은

ObOpenObjectByPointer()/ObOpenObjectByName() 을 호출하는 방법이 있습니다. 따라서 프로세스를 종료하지 못하게 막으려면 이 함수를 후킹해야 합니다.

ZwCreateThread() : 프로세스에 새로운 쓰레드를 만드는 함수인데, 아래와 같은 방법으로 프로세스를 종료할 수 있으므로 이 함수를 후킹해야 프로세스를 종료/접근 에서 막을 수 있습니다.

(간단하게 알기 쉽게 CreateThread()를 이용해서 설명하겠습니다.)

DWORD ProcessTerminateRoutine(PVOID pContext)

{

       return TerminateProcess((HANDLE)0xFFFFFFFF, 0);

}

 void RemoteTerminateProcess(HANDLE hProcess)

{

         HANDLE hThread=CreateThread(hProcess, ..., ProcessTerminateRoutine, ...);

         CloseHandle(hThread);

}

ZwOpenThread() : ZwOpenProcess()를 후킹했다 해도, 쓰레드에 접근 가능하다면 쓰레드 핸들을 얻어 온 후, 쓰레드를 종료해 버리면 프로세스는 종료됩니다. 그리하여 이 함수 역시 후킹할 필요가 있습니다.

ZwTerminateThread() : ZwOpenThread()를 후킹하는 이유와 거의 일치합니다.

ZwQuerySystemInformation() : 이 함수로 시스템의 정보를 알 수 있습니다. 그리고 프로세스/스레드 정보 역시 이 함수를 통해 얻을 수 있습니다(리스트로). 그리하여 이 함수를 후킹하여 SystemInformationBuffer에서 보호하려는 프로세스와의 링크를 끊어 주면 프로세스를 은폐함으로써 프로세스를 보호할 수 있습니다.

이외에도 ZwReadVirtualMemory()/ZwWriteVirtualMemory()/ZwQueryInformationProcess() 등등을 후킹할 필요도 있습니다.


이 방법들의 단점은, 여러 개의 함수를 후킹해야 한다는 점과 SDT Hook일 경우, SDT를 Restore하거나 Relocate되었을 경우, 혹은 이미지 파일에서 주소를 구해올 경우 무력화될수 있다는 점입니다.


2. EPROCESS 구조체의 ActiveProcessLinks 링크를 끊는다.

EPROCESS 구조체에는 ActiveProcessLinks가 있는데, 이것을 Tracing하면 프로세스 리스트를 얻을 수 있습니다.

그러나, 이 Link를 끊는다면, 프로세스는 은폐하여 보이지 않게 되어 프로세스를 보호할 수 있습니다.

이 방법의 단점은, Handle Table을 Tracing 할 경우, 혹은 무작위로 OpenProcess() 시도시, 들킬 수 있다는 것이 단점입니다.


3. PspCidTable의 주소를 구해서 프로세스의 흔적을 지워버린다.

PspCidTable에는 프로세스의 핸들에 대한 정보가 담겨 있습니다.

그런데, PspCidTable에서 프로세스의 핸들을 지워 버린다면, ZwOpenProcess()로 프로세스를 열지라도, 핸들이 지워졌기 때문에 프로세스 핸들을 얻어 오는 데에 실패할 것입니다.

결국, 프로세스는 보호됩니다.(FU_RootKit이 사용했던 방법입니다.)

이 방법의 단점은 PspCidTable의 주소를 직접 구해 와야 한다는 것과, 이 방법으로도 프로세스를 완전히 흔적을 없앨 수 없습니다.


4. ObOpenObjectByName() Hook

프로세스 핸들을 얻어 오는 방법 중 하나는 ObOpenObjectByName()을 호출하여 이름으로 프로세스 핸들을 얻어 오는 것입니다.

이 함수를 후킹한다면, 특정 이름의 Object를 여는 것을 막아서 프로세스를 보호할 수 있을 것입니다.

단점은 핸들이 어떤 Type의 핸들인지를 알 수 있어야 한다는 것(ObjectType이 NULL일 때), ObjectName을 어떻게 구분지을 것인지를 알아야 하고, 좀더 막으려면 PsLookupXxx()등의 함수를 보조적으로 후킹해야 한다는 것이 있습니다.


5. ObOpenObjectByPointer() Hook

이 함수 역시 프로세스 핸들을 얻어 올 수 있습니다. 프로세스 핸들을 얻을 때, Parameter 중 Object에는 EPROCESS의 포인터가 들어가고, ObjectType에는 PsProcessType이 들어갑니다(NULL이 들어가기도 한다). 그리하여 후킹한 후 내부적으로 인자를 검사해서 프로세스를 여는 것일 경우 막으면 프로세스를 보호할 수 있습니다.

단점은 핸들이 어떤 Type의 핸들인지를 알 수 있어야 한다는 것(ObjectType이 NULL일 때)과 좀더 막으려면 PsLookupXxx()등의 함수를 보조적으로 후킹해야 한다는 것이 있습니다.(4번과 비슷)


6. KeAttachProcess()/KeStackAttachProcess() Hook

이 함수는 프로세스에 Attach할 수 있는 함수입니다. Attach하면.....일단 프로세스에 접근 할 수 있을 테니 종료도 가능할 것입니다.

그러므로 이 함수를 후킹할 필요가 있습니다.(GameGuard에서 후킹하는 함수라죠)

단점은 인자에 EPROCESS 구조체의 포인터가 필요하므로 PsLookupXxx()등의 함수도 후킹해야 합니다.


7. PspTerminateProcess() Hook

이 함수는 ntoskrnl.exe가 내부적으로 가지고 있는 하나의 내부함수로서, 프로세스를 종료할 때 쓰이나, ZwTerminateProcess()와의 차이점은 첫번째 인자에 ProcessHandle 대신 Process Object의 포인터(EPROCESS 포인터)가 들어간다는 것입니다.

이 함수는 Unexported된 함수이며, 주소를 직접 구해 오는 수밖에 없습니다. 이 함수를 후킹하면 프로세스를 종료에서 보호할 수 있습니다.

단점은 무엇보다도 PspTerminateProcess()의 주소를 직접 구해와야 하는데에 있을거라 생각됩니다. (필자가 시도중)


8. PspTerminateThreadByPointer() Hook

이 함수는 ntoskrnl::NtTerminateProcess()과 ntoskrnl::NtTerminateThread()가 내부적으로 호출하는 함수이고, PspTerminateProcess() 역시 이 함수를 내부적으로 사용합니다.

PspTerminateProcess()는 PsGetNextProcessThread()를 사용하여 프로세스의 Next Thread Object Pointer를 얻어 온 후(ETHREAD의 포인터), PspTerminateThreadByPointer()를 호출하여 프로세스가 소유하는 쓰레드를 하나하나씩 종료합니다.

이 함수 역시 Unexported된 함수이며, 주소를 직접 구해 와야 합니다. 이 함수는 PspTerminateProcess() 조차도 내부적으로 호출하는 함수이므로 후킹하면 프로세스/쓰레드를 종료에서 보호할 수 있습니다.

단점 역시 7번과 비슷합니다.


이상으로 몇몇 안되지만, 프로세스를 보호하는 여러 방법과 그 단점에 대해 알아보았습니다.

Posted by skensita
Programming/Win32 API2008. 12. 3. 11:03

#include <stdio.h>
#include <conio.h>
#include <windows.h>

typedef long   NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
 SystemInformationClassMin=0,
 SystemBasicInformation=0,
 SystemProcessorInformation=1,
 SystemPerformanceInformation=2,
 SystemTimeOfDayInformation=3,
 SystemPathInformation=4,
 SystemNotImplemented1=4,
 SystemProcessInformation=5,
 SystemProcessesAndThreadsInformation=5,
 SystemCallCountInfoInformation=6,
 SystemCallCounts=6,
 SystemDeviceInformation=7,
 SystemConfigurationInformation=7,
 SystemProcessorPerformanceInformation=8,
 SystemProcessorTimes=8,
 SystemFlagsInformation=9,
 SystemGlobalFlag=9,
 SystemCallTimeInformation=10,
 SystemNotImplemented2=10,
 SystemModuleInformation=11,
 SystemLocksInformation=12,
 SystemLockInformation=12,
 SystemStackTraceInformation=13,
 SystemNotImplemented3=13,
 SystemPagedPoolInformation=14,
 SystemNotImplemented4=14,
 SystemNonPagedPoolInformation=15,
 SystemNotImplemented5=15,
 SystemHandleInformation=16,
 SystemObjectInformation=17,
 SystemPageFileInformation=18,
 SystemPagefileInformation=18,
 SystemVdmInstemulInformation=19,
 SystemInstructionEmulationCounts=19,
 SystemVdmBopInformation=20,
 SystemInvalidInfoClass1=20,
 SystemFileCacheInformation=21,
 SystemCacheInformation=21,
 SystemPoolTagInformation=22,
 SystemInterruptInformation=23,
 SystemProcessorStatistics=23,
 SystemDpcBehaviourInformation=24,
 SystemDpcInformation=24,
 SystemFullMemoryInformation=25,
 SystemNotImplemented6=25,
 SystemLoadImage=26,
 SystemUnloadImage=27,
 SystemTimeAdjustmentInformation=28,
 SystemTimeAdjustment=28,
 SystemSummaryMemoryInformation=29,
 SystemNotImplemented7=29,
 SystemNextEventIdInformation=30,
 SystemNotImplemented8=30,
 SystemEventIdsInformation=31,
 SystemNotImplemented9=31,
 SystemCrashDumpInformation=32,
 SystemExceptionInformation=33,
 SystemCrashDumpStateInformation=34,
 SystemKernelDebuggerInformation=35,
 SystemContextSwitchInformation=36,
 SystemRegistryQuotaInformation=37,
 SystemLoadAndCallImage=38,
 SystemPrioritySeparation=39,
 SystemPlugPlayBusInformation=40,
 SystemNotImplemented10=40,
 SystemDockInformation=41,
 SystemNotImplemented11=41,
 /* SystemPowerInformation=42, Conflicts with POWER_INFORMATION_LEVEL 1 */
 SystemInvalidInfoClass2=42,
 SystemProcessorSpeedInformation=43,
 SystemInvalidInfoClass3=43,
 SystemCurrentTimeZoneInformation=44,
 SystemTimeZoneInformation=44,
 SystemLookasideInformation=45,
 SystemSetTimeSlipEvent=46,
 SystemCreateSession=47,
 SystemDeleteSession=48,
 SystemInvalidInfoClass4=49,
 SystemRangeStartInformation=50,
 SystemVerifierInformation=51,
 SystemAddVerifier=52,
 SystemSessionProcessesInformation=53,
 SystemInformationClassMax
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_HANDLE_INFORMATION {
 ULONG ProcessId;
 UCHAR ObjectTypeNumber;
 UCHAR Flags;
 USHORT Handle;
 PVOID Object;
 ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef struct _SYSTEM_ALL_HANDLES_INFORMATION {
 ULONG NumberOfHandles;
 SYSTEM_HANDLE_INFORMATION *SystemHandleInformation;
} SYSTEM_ALL_HANDLES_INFORMATION;

typedef NTSTATUS (NTAPI *NTQUERYSYSTEMINFORMATION)(
 IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
 IN OUT PVOID SystemInformation,
 IN ULONG SystemInformationLength,
 OUT PULONG ReturnLength OPTIONAL);

typedef enum _DEBUG_CONTROL_CODE {
 DebugGetTraceInformation=1,
 DebugSetInternalBreakpoint,
 DebugSetSpecialCall,
 DebugClearSpecialCalls,
 DebugQuerySpecialCalls,
 DebugDbgBreakPoint,
 DebugMaximum,
 DebugReadVirtualMemory,
 DebugWriteVirtualMemory
} DEBUG_CONTROL_CODE;

typedef NTSTATUS (NTAPI *NTSYSTEMDEBUGCONTROL)(
 IN DEBUG_CONTROL_CODE ControlCode,
 IN PVOID InputBuffer OPTIONAL,
 IN ULONG InputBufferLength,
 OUT PVOID OutputBuffer OPTIONAL,
 IN ULONG OutputBufferLength,
 OUT PULONG ReturnLength OPTIONAL);

typedef struct _MEMORY_CHUNKS {
 PVOID VirtualAddress;
 PVOID Buffer;
 ULONG BufferSize;
} MEMORY_CHUNKS, *PMEMORY_CHUNKS;

typedef LONG NTSTATUS; 
typedef LONG KPRIORITY; 
typedef ULONG_PTR KAFFINITY; 

typedef enum _PROCESS_INFORMATION_CLASS { 
 ProcessBasicInformation,         // 0 Y N 
 ProcessQuotaLimits,              // 1 Y Y 
 ProcessIoCounters,               // 2 Y N 
 ProcessVmCounters,               // 3 Y N 
 ProcessTimes,                    // 4 Y N 
 ProcessBasePriority,             // 5 N Y 
 ProcessRaisePriority,            // 6 N Y 
 ProcessDebugPort,                // 7 Y Y 
 ProcessExceptionPort,            // 8 N Y 
 ProcessAccessToken,              // 9 N Y 
 ProcessLdtInformation,           // 10 Y Y 
 ProcessLdtSize,                  // 11 N Y 
 ProcessDefaultHardErrorMode,     // 12 Y Y 
 ProcessIoPortHandlers,           // 13 N Y 
 ProcessPooledUsageAndLimits,     // 14 Y N 
 ProcessWorkingSetWatch,          // 15 Y Y 
 ProcessUserModeIOPL,             // 16 N Y 
 ProcessEnableAlignmentFaultFixup,// 17 N Y 
 ProcessPriorityClass,            // 18 N Y 
 ProcessWx86Information,          // 19 Y N 
 ProcessHandleCount,              // 20 Y N 
 ProcessAffinityMask,             // 21 N Y 
 ProcessPriorityBoost,            // 22 Y Y 
 ProcessDeviceMap,                // 23 Y Y 
 ProcessSessionInformation,       // 24 Y Y 
 ProcessForegroundInformation,    // 25 N Y 
 ProcessWow64Information          // 26 Y N 
} PROCESS_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
 USHORT Length;
 USHORT MaximumLength;
 PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _RTL_USER_PROCESS_PARAMETERS {
 BYTE Reserved1[56];
 UNICODE_STRING ImagePathName;
 UNICODE_STRING CommandLine;
 BYTE Reserved2[92];
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

typedef struct _PEB_LDR_DATA {
 BYTE Reserved1[20];
 LIST_ENTRY InMemoryOrderModuleList;
 BYTE Reserved2[8];
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _PEB {
 BYTE Reserved1[2];
 BYTE BeingDebugged;
 BYTE Reserved2[9];
 PPEB_LDR_DATA LoaderData;
 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
 BYTE Reserved3[448];
 ULONG SessionId;
} PEB, *PPEB;

typedef struct _PROCESS_BASIC_INFORMATION {
 NTSTATUS ExitStatus;
 PPEB PebBaseAddress;
 KAFFINITY AffinityMask;
 KPRIORITY BasePriority;
 ULONG UniqueProcessId;
 ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;

typedef NTSTATUS (NTAPI *NTQUERYINFORMATIONPROCESS)(
 IN HANDLE ProcessHandle,
 IN PROCESS_INFORMATION_CLASS ProcessInformationClass,
 OUT PVOID ProcessInformation,
 IN ULONG ProcessInformationLength,
 OUT PULONG ReturnLength);

typedef PVOID  PEPROCESS;

NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;
NTSYSTEMDEBUGCONTROL NtSystemDebugControl;
NTQUERYINFORMATIONPROCESS NtQueryInformationProcess;
ULONG PidOffset;
ULONG PLinkOffset;

NTSTATUS KdReadVirtualMemory(PVOID VirtualAddress, PVOID Buffer, ULONG BufferSize)
{
 MEMORY_CHUNKS MemoryChunks;
 MemoryChunks.VirtualAddress=VirtualAddress;
 MemoryChunks.Buffer=Buffer;
 MemoryChunks.BufferSize=BufferSize;
 return NtSystemDebugControl(DebugReadVirtualMemory, &MemoryChunks, sizeof(MemoryChunks),
  NULL, 0, NULL);
}

NTSTATUS KdWriteVirtualMemory(PVOID VirtualAddress, PVOID Buffer, ULONG BufferSize)
{
 MEMORY_CHUNKS MemoryChunks;
 MemoryChunks.VirtualAddress=VirtualAddress;
 MemoryChunks.Buffer=Buffer;
 MemoryChunks.BufferSize=BufferSize;
 return NtSystemDebugControl(DebugWriteVirtualMemory, &MemoryChunks, sizeof(MemoryChunks),
  NULL, 0, NULL);
}

ULONG KdGetProcessId(HANDLE ProcessHandle)
{
 PROCESS_BASIC_INFORMATION ProcessInformation;
 if(NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation,
  &ProcessInformation, sizeof(PROCESS_BASIC_INFORMATION), NULL)>=0)
 {
  return (ULONG)ProcessInformation.UniqueProcessId;
 }
 return 0;
}

ULONG GetProcessIdOffset(PEPROCESS Process, HANDLE ProcessHandle)
{
 ULONG *Buffer=(PULONG)malloc(4096), ProcessId, Offset;
 memset(Buffer, 0, 4096);
 KdReadVirtualMemory(Process, Buffer, 4096);
 ProcessId=KdGetProcessId(ProcessHandle);
 if(!ProcessId)
 {
  free(Buffer);
  return 0;
 }
 for(Offset=0;Offset<4096;Offset++)
 {
  if(*(PULONG)((PCHAR)Buffer+Offset)==ProcessId)
  {
   free(Buffer);
   return Offset;
  }
 }
 free(Buffer);
 return 0;
}

ULONG GetProcessIdOffsetEx()
{
 ULONG NumberOfHandles, HandleIndex;
 ULONG *SystemInformation=(ULONG *)malloc(sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG));
 //미리 핸들을 하나 열어둔다.
 OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
    if(NtQuerySystemInformation(SystemHandleInformation, SystemInformation, sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG), 0)<0)
 {
  NumberOfHandles=*SystemInformation;
  free(SystemInformation);
  SystemInformation=(PULONG)malloc(NumberOfHandles*sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG));
 }
 NtQuerySystemInformation(SystemHandleInformation, SystemInformation, NumberOfHandles*sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG), 0);
 NumberOfHandles=*SystemInformation;
 printf("Number Of Handles : %d\n", NumberOfHandles);
 
 PSYSTEM_HANDLE_INFORMATION SystemHandleList=(PSYSTEM_HANDLE_INFORMATION)(SystemInformation+1);
 for(HandleIndex=0;HandleIndex<NumberOfHandles;HandleIndex++)
 {
  if(SystemHandleList[HandleIndex].ObjectTypeNumber==5 &&
   SystemHandleList[HandleIndex].ProcessId==GetCurrentProcessId())
  {
   ULONG Offset=GetProcessIdOffset(
    SystemHandleList[HandleIndex].Object,
    (HANDLE)SystemHandleList[HandleIndex].Handle);
   CloseHandle((HANDLE)SystemHandleList[HandleIndex].Handle);
   free(SystemInformation);
   return Offset;
  }
 }
 free(SystemInformation);
 return 0;
}

BOOL SetProcessId(ULONG ProcessId, ULONG NewProcessId)
{
 ULONG NumberOfHandles, HandleIndex;
  HANDLE ProcessHandle;
 ULONG *SystemInformation;
 //핸들을 미리 열어 둔다.
 ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
 if(!ProcessHandle)
 {
  return FALSE;
 }
 SystemInformation=(ULONG *)malloc(sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG));
 if(NtQuerySystemInformation(SystemHandleInformation, SystemInformation, sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG), 0)<0)
 {
  NumberOfHandles=*SystemInformation;
  free(SystemInformation);
  SystemInformation=(PULONG)malloc(NumberOfHandles*sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG));
 }
 NtQuerySystemInformation(SystemHandleInformation, SystemInformation, NumberOfHandles*sizeof(SYSTEM_HANDLE_INFORMATION)+sizeof(ULONG), 0);
 NumberOfHandles=*SystemInformation;
 
 PSYSTEM_HANDLE_INFORMATION SystemHandleList=(PSYSTEM_HANDLE_INFORMATION)(SystemInformation+1);
 printf("PID\tHANDLE\tObject\t\tFlags\tAccess\tObjectType\n");
 printf("-----------------------------------------------------------\n");
 for(HandleIndex=0;HandleIndex<NumberOfHandles;HandleIndex++)
 {
  //ObjectTypeNumber=5이면 프로세스, 6이면 스레드
  if(SystemHandleList[HandleIndex].ObjectTypeNumber==5 &&
   SystemHandleList[HandleIndex].ProcessId==GetCurrentProcessId() &&
   (HANDLE)SystemHandleList[HandleIndex].Handle==ProcessHandle)
  {
   //EPROCESS의 주소는 Object에 있다.
   //EPROCESS::UniqueProcessId에 NewProcessId의 값으로 채운다.
   KdWriteVirtualMemory(
    (PCHAR)SystemHandleList[HandleIndex].Object+PidOffset,
    &NewProcessId,
    4);
   printf("%d\t%x\t%x\t%x\t%x\t%x\n",
    SystemHandleList[HandleIndex].ProcessId,
    SystemHandleList[HandleIndex].Handle,
    SystemHandleList[HandleIndex].Object,
    SystemHandleList[HandleIndex].Flags,
    SystemHandleList[HandleIndex].GrantedAccess,
    SystemHandleList[HandleIndex].ObjectTypeNumber);
   CloseHandle((HANDLE)SystemHandleList[HandleIndex].Handle);
   free(SystemInformation);
   return TRUE;
  }
 }
 free(SystemInformation);
}

BOOL SetDebugPrivilege()
{
 HANDLE TokenHandle;
 LUID Luid;
 TOKEN_PRIVILEGES TokenPrivileges;
 if(!OpenProcessToken((HANDLE)0xFFFFFFFF, TOKEN_ALL_ACCESS, &TokenHandle))
 {
  return FALSE;
 }
// LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid);
 Luid.LowPart=20;
 Luid.HighPart=0;
 TokenPrivileges.PrivilegeCount=1;
 TokenPrivileges.Privileges->Attributes=SE_PRIVILEGE_ENABLED;
 TokenPrivileges.Privileges->Luid=Luid;
 if(!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES),
  NULL, NULL))
 {
  CloseHandle(TokenHandle);
  return FALSE;
 }
 CloseHandle(TokenHandle);
 return TRUE;
}

void main()
{
 ULONG ProcessId, NewProcessId;
 NtQuerySystemInformation=(NTQUERYSYSTEMINFORMATION)
  GetProcAddress(LoadLibrary("ntdll.dll"), "NtQuerySystemInformation");
 NtSystemDebugControl=(NTSYSTEMDEBUGCONTROL)
  GetProcAddress(LoadLibrary("ntdll.dll"), "NtSystemDebugControl");
 NtQueryInformationProcess=(NTQUERYINFORMATIONPROCESS)
  GetProcAddress(LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");
 SetDebugPrivilege();
 PidOffset=GetProcessIdOffsetEx();
 PLinkOffset=PidOffset+0x04;
 printf("변경할 프로세스 아이디 입력\n");
 scanf("%d", &ProcessId);
 printf("새로운 프로세스 아이디 입력\n");
 scanf("%d", &NewProcessId);
 SetProcessId(ProcessId, NewProcessId);
 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
 printf("원래대로 하려면 아무키나 누르시오\n");
 getch();
 SetProcessId(ProcessId, ProcessId);
}

Posted by skensita
Hacking & Security/Kernel2008. 11. 18. 16:31
 최근 대부분의 보안제품이나 웜바이러스가 유행처럼 SSDT 후킹기법을 사용해서 작동합니다. 이제는 더이상 SSDT 후킹도 최신기술이 아닌 범용기술이 되었습니다. 그래서 점점더 보안소프트웨어와 해킹툴/웜바이러스간의 장벽이 사라지고 있다고 보여집니다. 다양한 해킹기법을 사용한 보안 소프트웨어도 점점더 웜바이러스처럼 견고해지고 있습니다만, 이것은 결국 닭이 먼저나 달걀이 먼저냐의 문제로 보여집니다. 그래서 최근에는 탐지가 쉬운 SSDT 후킹대신에 DKOM 기법을 사용하여 SSDT 후킹 탐지에 걸리지 않는 기법들을 사용하는 추세입니다. DKOM은 의미그대로 직접적으로 커널 오브젝트를 조작하는 방법으로 SSDT 후킹으로 프로세스 숨기기나 네트워크 포트 연결 숨기기가 같은 스텔스 기능을 똑같이 구현할 수 있습니다.
DKOM이 좋은 한가지 예는 보안소프트웨어 자체를 외부의 디버깅이나 프로세스 어태치로부터 자신을 보호하기 위해서 자기자신을 프로세스에서 안보이게 하여 리버싱을 방지하려고 하는 노력도 한가지 예가 될 수있습니다.

1) DKOM으로 숨기기 기법을 한마디로 요약하면
  • 모든 운영체제는 메모리에 계정정보를 보통 구조체 형태나 오브젝트 형태로 저장합니다.
  • 구동되고 있는 프로세스 리스트를 알아내기 위한 함수가 바로 ZwQuerySystemInfomation() 함수입니다.
  • 프로세스 리스트는 EPROCESS 구조체라는 Doubly Linked List 형태로 구성되어 있습니다.
  • EPROCESS 구조체는 FLINK와 BLINK를 멤버로 하는 LIST_ENTRY 구조체를 가지고 있습니다.
  • 현재 구동중인 프로세스의 주소(EPROCESS  구조체)는 PsGetCurrentProcess 함수를 호출하여 찾을 수 있습니다.  
  • PsGetCurrentProcess함수의 실제 함수는 IoGetCurrentProcess 함수가 됨
  • 이 함수를 디스어셈블하면

             mov   eax,   fs:0x00000124;  <-- IoGetCurrentProcess 함수
             mov   eax,   [eax + 0x44];   <-- _EPROCESS의 오프셋
             ret

결론적으로 EPROCESS의 BLINK와 FLINK를 포인터를 조작해서 Rootkit 코드의 프로세스를 Skip하도록 FLINK, BLINK포인터를 조작하면 윈도우즈의 작업관리자(taskmgr.exe)에서 해당 Rootkit 프로세스가 보이지 않게 된다.
보통 SSDT 후킹으로 프로세스를 숨기는 경우에는 SSDT 후킹여부를 탐지 및 복구가 가능하지만 DKOM은 탐지가 휠씬 어렵다.  

2) 디바이스 드라이버 숨기기
로드된 디바이스 드라이버를 안보이게 하는 기법도 DKOM 기법을 사용해서 구현할 수 있습니다.
디바이스 드라이버 오브젝트인 DRIVER_OBECJ 구조체에서 오프셋 값 0x14 위치에 MODULE_ENTRY 오브젝트라고 하는 것이 있습니다. 이 MODULE_ENTRY 역시 Dubly linked 리스트 구조로 FLINK/BLINK로 각 드라이버 리스트를 정보를 파악하게 되는데 이 FLINK/BLINK 값을 조작하면 Rootkit 커널 드라이버를 드라이버 리스트에서 안보이게 할 수 있습니다.
   
    typedef  struct _MODUL_ENTRY {
         LIST_ENTRY module_list_entry;
         DWORD    unknown1[4];
         DWORD    base;
         DWORD    driver_start;
         DWORD    unknown2;
         UNICODE_STRING  driver_Path;
         UNICODE_STRING  driver_Name;

}   MODULE_ENTRY, *PMODULE_ENTRY;


출처 : http://coderant.egloos.com/3564872

Posted by skensita
Hacking & Security/System2008. 11. 14. 11:08

[source : http://blog.naver.com/ex______/50029410228 ]

커널단에서 프로세스를 보호하는 방법에 대해서 요약 작성 및 정리한 자료입니다.

 프로세스를 보호하는 드라이버를 거의 완벽에 가까울 정도로(?) 코딩하는 방법은 없을 지 고찰한 결과 아래와 같은 결론에 도달하게 되었습니다.

 목표 프로세스를 보호하는 방법

- 유저 모드 후킹

일단 기본적으로 프로세스를 조작하는 API인 NTDLL.DLL의 NtOpenProcess, NtTerminateProcess, NtReadVirtualMemory, NtWriteVirtualMemory, KERNEL32.DLL의 DebugActiveProcess 등이 후킹되어야 할 것입니다.
그리고 WINSTA.DLL의 WinStationTerminateProcess를 후킹하여 Terminal Service를 이용한 프로세스 종료를 차단합니다.

- 커널 후킹

ZwOpenProcess/ObReferenceObjectByPointer/PsLookupProcessByProcessId
ZwOpenProcess는 ObReferenceObjectByPointer를 호출하고 이는 다시 PsLookupProcessByProcessId를 호출하는데, 이들을 후킹하면 프로세스를 여는걸 어느정도 방지할 수 있을겁니다.

 KeAttachProcess / KeStackAttachProcess

일단 프로세스의 메모리를 읽고 쓸땐 이 API를 사용하여 Page를 대상 프로세스의 Page로 전환해야하는데, 이를 방지하는 역할을 합니다.

ZwReadVirtualMemory/ZwWriteVirtualMemory/ZwTerminateProcess

프로세스의 메모리를 보호하며, 프로세스를 종료로부터 보호합니다.

ZwOpenThread/ZwGetContextThread/ZwSetContextThread/ZwTerminateThread

쓰레드를 보호합니다. 또한 쓰레드의 종료를 차단합니다.

 ZwCreateFile (IoCreateFile)

이 것을 후킹하여 드라이버 혹은 프로세스 파일에 접근하는걸 차단할 수 있습니다.

경우에 따라선 ntoskrnl.exe를 접근하는 것을 막아 후킹을 해제하는걸 막아줄 수 있습니다.

 ZwQuerySystemInformation

이 것을 후킹하면 프로세스를 숨길 수 있습니다. 밑에서 소개하는 DKOM보다는 안정적입니다.

 
- Internal 함수 후킹

 PspTerminateThreadByPointer:

프로세스 종료의 가장 근본적인 API로 쓰레드를 종료시키는 가장 저수준의 API입니다. 따라서 이 함수를 후킹해놓으면 프로세스를 함부로 종료할 수 없습니다. 이 API는 NtTerminateThread()에서 ObfDereferenceObject()전에 호출되므로 코드바이트로부터 이를 추적해나갈 수 있습니다.

 KiAttachProcess:

KeAttachProcess/KeStackAttachProcess의 Internal 함수로 후킹할 수 있으면 해놓는게 좋습니다. KeAttachProcess()에서 호출하는데, 이것이 이미 후킹되어있었을 수도 있으므로, 파일로부터 구해와야 합니다. 이 방법은 제가 블로그에 쓴 적 있습니다.

참고: [Unexported Symbol] KiAttachProcess() 주소값 구하기.

 
- DKOM

 적절한 DKOM은 강력한 무기가 될 수 있습니다. 아래와 같은 방법을 사용함으로써 프로세스를 효과적으로 보호할 수 있습니다.

 1. DebugPort = NULL

디버거를 무력화하여 예외 처리를 프로세스에게 넘기는 방법으로, 디버거를 무력화시키는 방법중 하나입니다. 이 값이 NULL인지 아닌지 체크하는 방법도 비교적 괜찮은 방법이나, NT4/2000계열에서는 간혹 쓰레기값이 들어있는 경우가 있기 때문에 권장하지 않습니다.

 EPROCESS에서의 DebugPort의 Offset 구하는 방법은 대략 아래와 같이 코딩할 수 있습니다.

 VOID CalcHandleTableOffset(VOID)
{
 PEPROCESS Proc = IoGetCurrentProcess();
 HANDLE PID = PsGetCurrentProcessId();
 PVOID GuessHandleTable = NULL;
 int i = 0;
 for(i = 0; i < PAGE_SIZE; i+=4)
 {
  GuessHandleTable = *(PVOID *)((PCHAR)Proc + i);
  if(MmIsAddressValid(GuessHandleTable))
  {
   if(*(PHANDLE)((PCHAR)GuessHandleTable + 0x08) == PID)
   {
    HANDLE_TABLE_OFFSET = i;
    return;
   }
  }
 }
}

 CalcHandleTableOffset();

DEBUG_PORT_OFFSET = HANDLE_TABLE_OFFSET - 8;

 2. EPROCESS->ActiveProcessLinks Unlinking

프로세스를 숨겨서 보호합니다. 추가적으로 EPROCESS의 UniqueProcessId를 이상한 값으로 바꾸면 핸들을 얻는것을 어느정도 막을 수 있습니다. (물론 관련 ETHREAD 구조체에서 PID를 얻을수도 있기 때문에 이 방법이 꼭 좋은건 아닙니다.)

만약 프로세스 ID를 바꾸었다면 프로세스 종료 전에 복구하여야합니다.(그렇지 않으면 CID_HANDLE_DELETION 블루스크린을 맛보시게 될겁니다.)

그리고, 추가적으로 PspCidTable이라는 Unexported symbol에서 프로세스 Offset을 NULL로 만들어놓으면, 근본적으로 핸들을 얻을 수가 없을겁니다. PspCidTable은 PsLookupProcessByProcessId()에서 ExMapHandleToPointer(Ex)를 호출하기 전에 참조하는 심볼로, 바이트 코드를 트래버싱하거나, XP 이상에서는 KPCR->KdVersionBlock의 PspCidTable 필드를 참조해서 구할 수도 있습니다.

 3. EPROCESS::HandleTable Unlinking

숨겨진 프로세스를 찾는 대표적인 방법으로는 크게 두가지가 있습니다. EPROCESS::HandleTable를 traverse 하거나 PspCidTable을 트레버싱하는 방법입니다. 물론, ETHREAD를 이용하거나 기타 여러가지 방법(brute-force 등등)을 이용하면 뚫리긴 합니다만, HandleTable을 끊음으로서 더욱 찾기 힘들게 할 수 있습니다.

 위에서 언급한 ActiveProcessLinks와 핸들 테이블은 아래와 같은 LIST_ENTRY 구조입니다.

typedef struct _LIST_ENTRY {
    PLIST_ENTRY Flink;
    PLIST_ENTRY Blink;
} LIST_ENTRY, *PLIST_ENTRY;

 
드라이버를 효과적으로 숨기는 방법

 1. PsLoadedModuleList unlinking

PsLoadedModuleList는 DRIVER_OBJECT 구조체의 DriverSection에 의해 Point됩니다. 그리고 아래와 같은 구조를 가지고 있습니다.(출처: rootkit.com 책)

typedef struct _MODULE_ENTRY
{
 LIST_ENTRY ModuleListEntry;
 DWORD Unknown1[4];
 DWORD BaseAddress;
 DWORD DriverStart;
 DWORD Unknown2;
 UNICODE_STRING Driver_Path;
 UNICODE_STRING Driver_Name;
} MODULE_ENTRY, *PMODULE_ENTRY;

 
ModuleListEntry를 순회해서 BaseAddress가 DriverObject->DriverStart와 같은 엔트리를 찾아내서 ModuleListEntry를 끊어주면 숨겨줄 수 있을겁니다.

 2. IoGetDeviceObjectPointer Hook

심볼릭 링크로부터 DEVICE_OBJECT를 얻는 대표적인 방법으로 이를 후킹하면 DEVICE_OBJECT를 얻는걸 막을 수 있고, 결과적으로 드라이버를 2차적인 루트킷 스캐너로부터 숨길 수 있게됩니다. DEVICE_OBJECT는 DRIVER_OBJECT를 포인트하기 때문에, 필수적으로 후킹해야합니다.

 3. ZwOpenKey() Hook

드라이버 서비스 키를 traversing 하여 드라이버 정보를 얻는것을 차단할 수 있습니다.

 4. IoCreateFile() Hook

드라이버 파일을 접근하지 못하게 함으로써 드라이버에 대한 접근을 차단합니다. ZwCreateFile()은 IoCreateFile을 직접 호출하므로, IoCreateFile()을 후킹하면 탐지되기가 더 어려우며, 그만큼 효과를 발휘하게 됩니다.

 5. ** 잡소리 **

보호된 프로세스로부터 드라이버 언로드 메시지가 왔을 때만 드라이버 언로드 핸들러를 설정합니다. 이는, 제 3자가 드라이버를 그냥 언로드하는걸 막을 수 있습니다. 

그리고 DeviceIoControl 메시지는 보호 대상 혹은 클라이언트의 메시지만 신뢰하는 것이 가장 좋습니다. (즉 말하자면 나머지는 쌩~ 까버리자는거죠.)

끝으로..
저는 위 방법들이 꼭 최선 해결책이라고 생각하진 않으며..
더 좋은 방어법이 있거나, 혹은 이를 우회하는 공격법이 존재할 수 있습니다.
그러므로, 자신만의 공격법이나 방어법을 만드는 것이 가장 좋은 방법이라고
생각이 됩니다.

Posted by skensita