Microsoft Visual C++ 8.0 Runtime Library 의 Protection 기능.
주제 : Visual C++ 8.0 에서 새롭게 시작된 일종의 Protection 에 대한 내용이다.
내용 :
대부분의 사람들이 컴파일 된 실행 파일을 그대로 사용한다. 하지만, 여러가지 사정으로 인하여 그렇게 하지 못하는 사람도 있게 되는데, 여기서는 Packer 를 만들고 있는 사람들에 대하여 Visual C++ 8.0 이상에서 컴파일 한 바이너리에 대한 내용을 다루고자 한다.
이 내용은 꽤 오래된 고전 적인 내용이지만, 필자의 게으름으로 인하여 알지 못하고 있던 것 중 하나이다.
Protection 이라고 하는 것은 자신이 정상적인 상태(프로그래머가 의도한 대로 동작하는 상태)라는 것을 보장하기 위한 일종의 덫이다. 정상적인 상태에서만 있어야 할 것들을 체크하여, 정상적이지 않은 상태에 있는 경우에 '낙오' 를 시키는 것이 Protection 의 기본적인 컨셉이라고 할 수 있다. 여기서 Protection 에 대하여 이야기하는 것은 우리가 Protection 에 대하여 더 이상 방관자가 될 수 없기 때문이다.
이 말을 좀 더 자세하게 이야기하자면, 기존의 Protection 의 영역이 일부 프로그래머들에 의한 일부 Software 에서만 있어왔었다면, 이제 이것이 대부분의 프로그래머들이 사용하는 Visual C++ 에도 적용이 되었다는 이야기이다. 운영체제와 같은 저수준의 시스템 프로그래밍에 관심이 많은 프로그래머라면 운영체제의 일부 중요한 Software 의 경우에는 자체적으로 Anti-Debugging, Anti-Cracking 에 관련된 코드가 들어있다는 것을 알고 있을 것이다. 즉, 아주 오랜 예전 부터 자신을 보호하기 위한 기능들은, 우리 옆에 존재하여 왔었다. 10 여년 전의 Windows95 에서도 있었던 것이지만, 이러한 것들은 시스템의 깊숙한 곳을 분석하는 일부 프로그래머들이나 해커들에게만 알려져있었던 내용이다. 하지만, Visual C++ 8 이 나오면서 부터, 이러한 것이 더 이상 시스템의 깊숙한 곳에서만 존재하지 않고, 우리가 만드는 거의 대부분의 프로그램에 기본적으로 들어가는것이 되었다.
한가지 간단한 실험을 해 보면서 이 기능에 대해서 좀 더 자세하게 이해하여 보도록 하자.
먼저 Visual C++ 8 버전(2005)을 실행하여, MFC 프로젝트를 생성한다. 생성할 때, MFC 사용은 정적 라이브러리에서 MFC 를 사용하도록 한다.
그리고, 아래의 그림에서처럼, 속성 페이지에서 '문자 집합' 을 멀티바이트 문자 집합 사용으로 한다.
이제 다음과 같은 코드를 넣고 컴파일 한다.
void CMSVCRT80PROTECTDlg::OnBnClickedOk()
{
char pszTemp[1024];
sprintf_s(pszTemp, "Microsoft Visual C++ 8.0 Runtime Library Protection (%f)", rand());
AfxMessageBox(pszTemp);
}
그리고 실행을 하게 되면, 정상적으로 실행이 되는 것을 볼 수 있다. 이제, PE 파일의 섹션을 변경시켜 보도록 하자
위의 그림은, 실행 파일의 rdata 섹션의 속성에 Read/Write 옵션을 준 것이다. 이렇게 한 이후에 프로그램을 실행 시키면 다음과 같은 에러를 볼 수 있다.
이 이상한 에러는 Visual C++ 8.0 부터 발생하며, CRT 라이브러리 함수를 꼭 사용하여야 한다. 또한, floating point 에 관련된 코드가 있어야 한다.
일종의 Protection 인 이 기능은, 컴파일러가 컴파일을 할 때, C runtime library 에서 사용하는 데이터들이 Read only Section 에 들어가게 되는 경우, 실행시에도 Read only Section 에 존재하는지를 한번 더 검증하도록 하는 코드를 함께 컴파일 한다. 만약, 실제 실행시 이 검사가 실패하게 되면, Runtime library 는 위의 그림과 같은 에러를 내도록 하게 한다.
즉, 일반적인 프로그래머들이 PE 헤더를 바꿀 일 은 없으므로, 이러한 문제를 만날일은 없지만, PE 헤더를 바꿀 필요가 있는 특별한 프로그래머들은, 이러한 문제를 만날 수 있게 된다. 이 문제를 해결하기 위해서는, 관련 Runtime library 함수가 실행되기 전에 실행되고 있는 메모리 상의 PE 헤더의 값 중, 데이터가 있는 섹션(rdata)의 값을 처음 컴파일 되던 상태(보통 0x40000040) 으로 바꾸어 주어야 한다.