보통 함수를 작성할 때 다음과 같이 인자로 받은 포인터를 검증한다.
- BOOL SampleFunc(LPTSTR lpTargetBuffer, DWORD dwBufferSize)
- {
- if (lpTargetBuffer == NULL) // 인자 검증!!
- return FALSE;
- // 여기서부터 작업 시작!!
- // ...
- }
한 후배가 물었다.
"만약 NULL 이 아닌 잘못된 포인터가 넘어온 경우는 어떻게 체크하죠?"
당연한 듯이 대답해주었다.
"그걸 어떻게 체크하냐? 어쩔 수 없는 거지!!"
퇴근하면서 생각해 보니... 어쩔수 없지는... 않을 것 같아서 한번 만들어 보았다. ^^
- /*
- IsWritableMemory : 주어진 메모리 주소의 상태가 쓰기가능인지 체크하는 함수
- Param :
- LPVOID pMemoryAddr : 검사하고자 하는 메모리의 주소
- Return Value :
- ERROR_SUCCESS : 인자로 주어진 주소의 메모리가 Read/Write 가능한 경우
- 기타 값 : Read/Write 가능한 메모리가 아니면 해당 주소의 Protect Mode 를
- 나타내는 0이 아닌 값을 리턴함.
- http://msdn.microsoft.com/en-us/library/aa915370.aspx 참조
- */
- INT IsWritableMemory(LPVOID pMemoryAddr)
- {
- MEMORY_BASIC_INFORMATION MemInfo = {0,};
- SIZE_T nResult = 0;
- nResult = VirtualQuery (pMemoryAddr, &MemInfo, sizeof(MemInfo));
- if (nResult == 0) // 커널 영역인 경우 VirtualQuery 자체가 Fail함.
- {
- return -1;
- } else if (MemInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE) )
- {
- return ERROR_SUCCESS;
- } else
- {
- return MemInfo.Protect;
- }
- }
- /*
- IsReadableMemory : 주어진 메모리 주소의 상태가 참조(Read)가능인지 체크하는 함수
- Param :
- LPVOID pMemoryAddr : 검사하고자 하는 메모리의 주소
- Return Value :
- ERROR_SUCCESS : 인자로 주어진 주소의 메모리가 Read 가능한 경우
- 기타 값 : Read 가능한 메모리가 아니면 해당 주소의 State 를
- 나타내는 0이 아닌 값을 리턴함.
- http://msdn.microsoft.com/en-us/library/aa915370.aspx 참조
- */
- INT IsReadableMemory(LPVOID pMemoryAddr)
- {
- MEMORY_BASIC_INFORMATION MemInfo = {0,};
- SIZE_T nResult = 0;
- nResult = VirtualQuery (pMemoryAddr, &MemInfo, sizeof(MemInfo));
- if (nResult == 0) // 커널 영역인 경우 VirtualQuery 자체가 Fail함.
- {
- return -1;
- } else if (MemInfo.State & MEM_COMMIT)
- {
- return ERROR_SUCCESS;
- } else
- {
- return MemInfo.State;
- }
- }
사용법은 다음과 같다.
- BOOL SampleFunc(LPTSTR lpTargetBuffer, DWORD dwBufferSize)
- {
- if (IsWritableMemory(lpTargetBuffer) != ERROR_SUCCESS)
- {
- return FALSE; // 주어진 버퍼가 유효한 메모리가 아닐 경우
- }
- lstrcpyn(lpTargetBuffer, L"Some String", dwBufferSize);
- return TRUE;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- TCHAR szSafeBuffer[1024] = {0,}; // 정상적인 버퍼
- LPTSTR lpTest; // 초기화 되지 않은 포인터!!
- BOOL bResult = FALSE;
- bResult = SampleFunc(szSafeBuffer, _countof(szSafeBuffer));
- if (bResult != TRUE)
- {
- wprintf(L"Fail to Copy String\r\n");
- return FALSE;
- }
- wprintf(L"Result String : %s\r\n", szSafeBuffer);
- bResult = SampleFunc(lpTest, sizeof(lpTest)); // Access Violation??
- if (bResult != TRUE)
- {
- wprintf(L"Fail to Copy String\r\n");
- return FALSE;
- }
- wprintf(L"Result String : %s\r\n", lpTest);
- return TRUE;
- }
실행 결과는 다음과 같다
Result String : Some String
Fail to Copy String
Fail to Copy String
할당되지 않은 포인터를 버퍼 인자로 넘겼을 때 Access Violation이 발생하지 않고 Parameter Validation 과정에서 걸러지는 것을 확인할 수 있다.
생각나는대로 뚝딱 만든 것이니 지나가던 고수님께서 혹시 틀린 부분이라도 발견하신다면 알려주시라.
'C++' 카테고리의 다른 글
DLL Injection (4) | 2009.03.20 |
---|---|
UTF-8 String을 유니코드(UCS-16)로 전환하기 (0) | 2009.03.02 |
CriticalSection에 관한 알쏭달쏭 퀴즈! (4) | 2009.02.05 |
문자열 함수 "_s" 시리즈 분석 (_tcsncpy_s 등) (10) | 2008.09.29 |
MBCS 프로젝트 유니코드 전환 가이드 (0) | 2008.08.23 |