프로그래밍

XP 유저모드에서 커널메모리 접근(읽고쓰기)

friday13th 2011. 6. 22. 14:06

일반적으로 유저모드에서 커널메모리 접근은 차단되어있다.
단, 예외적으로 XP에서 관리자 권한을 가지고있고 SE_DEBUG_NAME 특근을 얻을 수 있으면
ntdll.dll 의 ZwSystemDebugControl Native API 함수를 이용하요 커널메모리에 접근이 가능하다.


 KernelMemory.h
#pragma once

//NTSTATUS값이 0이상이면 보통 성공한 상태를 의미한다
#define NT_SUCCESS(Status)    ((NTSTATUS)(Status) >= 0)
typedef LONG       NTSTATUS;

typedef struct _MEMORY_CHUNKS
{
 PVOID pVirtualAddress;
 PVOID pBuffer;
 DWORD dwBufferSize;
} MEMORY_CHUNKS, *PMEMORY_CHUNKS;

typedef enum _SYSDBG_COMMAND
{
 SysDbgCopyMemoryChunks_0 = 0x08,  //가상 메모리로부터 데이터를 읽을 때
 SysDbgCopyMemoryChunks_1 = 0x09,  //가상 메모리에 데이터를 쓸 때
} SYSDBG_COMMAND;
 
typedef NTSTATUS ( __stdcall *ZWSYSTEMDEBUGCONTROL ) ( IN SYSDBG_COMMAND SysDbgChunks,
               IN OUT PMEMORY_CHUNKS pQueryBuff,
               DWORD dwSize, DWORD, DWORD, NTSTATUS *pResult);

class CKernelMemory
{

 ZWSYSTEMDEBUGCONTROL  m_ZwSystemDebugControl;
public:
 CKernelMemory(void);
 ~CKernelMemory(void);
 BOOL Init(HANDLE hProcess);
 BOOL ReadVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize);
 BOOL WriteVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize);
protected:
 BOOL EnablePrivilege(LPCSTR lpName, HANDLE hProcess);
 BOOL GetNativeAPIAddress();

};

 


KenelMemory.cpp
#include "stdafx.h"
#include "KernelMemory.h"
CKernelMemory::CKernelMemory(void)
{
 
 m_ZwSystemDebugControl = NULL;
}
CKernelMemory::~CKernelMemory(void)
{
}
BOOL CKernelMemory::Init(HANDLE hProcess)
{
 if (!EnablePrivilege(SE_DEBUG_NAME, hProcess))
 {  
  return FALSE;
 }
 return GetNativeAPIAddress();
}
 
//Native API의 주소를 얻어온다
BOOL CKernelMemory::GetNativeAPIAddress()
{
 //ntdll.dll로드
 HMODULE hNTDll;
 if( (hNTDll = GetModuleHandle("ntdll.dll")) == NULL )
 {  
  if( (hNTDll = LoadLibrary("ntdll.dll")) == NULL )
  {   
   return FALSE;
  }
 }
 //ZwSystemDebugControl로드
 if( (m_ZwSystemDebugControl = (ZWSYSTEMDEBUGCONTROL)GetProcAddress(
  hNTDll, "ZwSystemDebugControl")) == NULL )
 {  
  return FALSE;
 } 
 return TRUE;
}
 

//가상 메모리 공간을 읽어온다
BOOL CKernelMemory::ReadVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize)
{
 NTSTATUS result;
 //메모리 청크 구조체
 MEMORY_CHUNKS QueryBuff;
 QueryBuff.pVirtualAddress = pAddress; //가상 메모리 주소
 QueryBuff.pBuffer = pBuffer;   //버퍼의 주소
 QueryBuff.dwBufferSize = dwBufferSize; //버퍼의 크기
 //가상 메모리 공간을 읽어서 버퍼에 기록한다
 m_ZwSystemDebugControl(SysDbgCopyMemoryChunks_0, &QueryBuff,
  sizeof(MEMORY_CHUNKS), NULL, 0, &result);
 return NT_SUCCESS(result);
}
//가상 메모리 공간에 데이터를 쓴다
BOOL CKernelMemory::WriteVirtualMemory(PVOID pAddress, PVOID pBuffer, DWORD dwBufferSize)
{
 NTSTATUS result;
 //메모리 청크 구조체
 MEMORY_CHUNKS QueryBuff;
 QueryBuff.pVirtualAddress = pAddress; //가상 메모리 주소
 QueryBuff.pBuffer = pBuffer;   //버퍼의 주소
 QueryBuff.dwBufferSize = dwBufferSize; //버퍼의 크기
 //버퍼로 부터 값을 읽어서 가상 메모리 공간에 기록한다
 m_ZwSystemDebugControl(SysDbgCopyMemoryChunks_1, &QueryBuff,
  sizeof(MEMORY_CHUNKS), NULL, 0, &result);
 return NT_SUCCESS(result);
}
 
BOOL CKernelMemory::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;
}