왜 프로그램을 보호해야 하는가
이전에 썼던 글에서처럼 싱글 게임일 경우 직접적인 피해는 없습니다.
문제는 온라인 게임 등 사람들 간의 경쟁 게임이거나 수많은 돈이 오고 가고 하는 프로그램은
이러한 부정 방법을 쓸 수 없게 해야 하겠죠. 치트가 허용 되면 공정성이 없으니까요.
물론 치트를 사용하는 유저를 하나하나 다 잡을 수 있으면 제일 좋겠지만 사실 이 방법은
다들 아시다시피 힘든 편입니다. 만성적인 인력 부족의 이유가 제일 크겠지만요.
이러한 과정에서 리버싱을 하기 어렵게끔, 혹은 거의 불가능하게끔 하는 기술들이
매우 중요해졌다고 볼 수 있습니다. 내용물을 볼 수 없다면 치트도 못 쓸 테니까요.
보호하는 입장에서도 리버싱은 필수다
리버싱이라는 걸 치터들이나 악성코드 개발자들만이 다룬다고 아는 분들도 있습니다.
하지만 보안 개발자들도 리버싱은 필수로 해야 하는 게 요즘은 악성코드나 핵 개발자들도
자기 프로그램의 보안을 위해 많은 노력을 거치고 있습니다. 보안 개발자들도 시중에 나온
악성코드나 핵 프로그램 등이 어떻게 돌아가는지를 알아야 막을 수 있기 때문에 말이죠.
결국 창과 방패의 싸움이라는 말이 딱 맞는 셈입니다. 서로 공격, 방어를 주고받으니까요.
코드를 보호하는 기법
잘 알려진 방식엔 크게 3가지가 있습니다.
- Packer(패커) // 이전 글에서도 잠깐 등장
- Obfuscator(난독화)
- Protector (Virtualization, 가상화)
1. 패커
패커는 지난 글에서도 잠깐 다뤘지만 정적 분석을 하는 방식을 막기 위해 쓰는 도구입니다.
정적 분석의 대표적인 프로그램으로는 Ollydbg가 있습니다. 파일이 실행되기 전에 내용을
볼 수 없도록 막는 방법이죠.
지난 글에서 Cheat Engine을 사용할 경우, 정적이 아닌 동적인 분석이 이뤄지기 때문에
패킹이 안 풀린 모습을 볼 일은 잘 없다고 했는데 안 풀린 모습을 보면 요렇게 생겼습니다.
이런 식으로 API나 이런 곳들은 다 정상적으로 메모리가 올라와 있습니다.
정작 클라이언트 첫 명령어가 박혀있어야 할 곳은 뭔 알 수 없는 메모리로 박혀 있습니다.
프로그램이 정상적으로 실행이 안 됐기에 패킹이 안 풀린 것입니다. 이러면 내용을 아예 못 보죠.
하지만 패킹의 경우 그냥 프로그램을 실행해서 메모리가 올라간 후 덤프를 떠서 본다는 등의
파훼법이 있기 때문에 패킹만 하는 경우는 없습니다.
2. 난독화
난독화는 코드의 변수나 클래스, 문자열 등을 해석하기 어렵게 변경해놓거나 실행을 안 하는
일명 쓰레기 코드(Garbage Code) 등을 배치하여 가독성을 떨어뜨려서 리버서 입장에서
분석하는 시간을 지연시키는 방법입니다.
이전 글들 중에서 이미 한 번 나왔었습니다.
위의 글을 보시면 문자열을 프로그래머가 만들어 낸 규칙에 따라 암호화된 것을 풀고 있는 걸 볼 수 있습니다.
이런 식으로 문구를 암호화시키는 행위도 난독화에 해당합니다. 크랙미에서의 문제들이 대부분 난독화를
이용하여 리버서의 코드 가닥을 떨어뜨리는 행위로 이루어집니다.
또한 여태껏 글들을 보시면 Module+XX 이런 식으로 사람이 쉽게 알게끔 표기된 정보들이 있는데
이를 변경하거나 랜덤으로 바꾸는 식으로 가독성을 떨어뜨리기도 합니다.
3. Protector
위의 2가지 방법을 섞어 쓰며, 안티 디버깅이나 커널 드라이버 감지 등 분석 환경 감지
그리고 코드 가상화의 기법을 사용합니다. 사실상 끝판왕에 해당하겠죠.
안티 디버깅의 경우 제가 거의 모든 글에서 디버거를 사용했었죠.
이 디버거를 사용할 수 없도록 디버깅 중인 상태를 감지한다는 등 하는 방식입니다.
기본적인 디버깅을 사용할 수 없을 때 커널 쪽으로 드라이버를 올려서 커널 디버깅을
사용하는데, 이 관련 드라이버들을 감지하는 등이 분석 환경 감지에 해당합니다.
하지만 커널 드라이버 감지의 경우 OS마다 커널 구조가 다르기 때문에 이를 다 아우르는
감지 체계는 제 경험상으론 못 본 거 같습니다.
결국 커널 드라이버까지 사용을 동원한다 하면 디버깅하는 행위 자체를 막는 건
힘들기 때문에 이 가상화라는 기술까지 사용을 하는 것이겠죠.
코드 가상화라 하면 원래의 명령어들을 가상화시키고 이 가상화된 코드를 본체 CPU가
처리하는 게 아닌 개발자가 만든 소프트웨어적인 핸들러가 처리하는 되는 기술입니다.
그러니까 정리를 하자면 다음과 같습니다.
바이트코드 -> Virtual CPU -> 하드웨어 CPU
이 순서로 실행이 되어서 가상화되었음에도 정상적으로 프로그램은 실행이 되는 것입니다.
코드 가상화에도 많은 프로그램들이 있는데 각 프로그램들마다 가상화하는 방식들은 다릅니다.
실제 사용되고 있는 예시를 하나 가져와보면 이렇습니다.
보시면 시작 부분은 저희가 아는 어셈 명령어인 거 같은데 밑 부분부터 이상합니다.
어딘가로 jmp를 하게 되어 있고 밑에는 다 깨져 있습니다.
조금 더 내려봐도 전부 깨져있습니다.
일반적인 명령어들이 아니라 패킹이 안 풀린 것 마냥 어셈이 다 깨져있습니다.
즉 이 안에 있는 명령어들과 호출되는 함수들을 제작자가 의도하고 숨긴 거라 볼 수 있겠습니다.
그러면 결국 분석이든 뭐든 시도를 해보려면 이 jmp를 타고 넘어가야 할 것입니다.
jmp를 타고 넘어왔더니 push와 pop 명령어를 난사해서 스택부터 다 부숴버리네요.
저희가 알고 있는 일반적인 명령어 체계가 전혀 아닌 겁니다.
이러한 경우 이 가상화 코드를 만드는 로직을 알아내거나 원본이 없으면 이 내용은
알 수가 없습니다. 뭐 이 가상화된 함수도 어딘가에서 호출을 받고 뭐 하긴 할 테니
그런 단편적인 정보 습득이 전부일 것입니다. 그 단편적인 정보로 안의 함수 내용이
어떤 거일지 추리를 해야 되는 그런 영역에 도달을 하죠...
그렇기 때문에 이전 글들에서 한 두 번씩 말했던 대로 리버싱은 막일의 향연입니다.
결국 얼마나 오래 붙들어 매서 쳐다보냐의 싸움인 거예요...
예시로 다뤄볼 만한 프로그램들을 찾아보긴 할 텐데 있으면 한 번 자세하게 소개를 해보고
없으면 제가 어쩔 수 없이 온라인 게임들로 예시를 들어야 할텐데 그러진 않았으면 좋겠네요..
'Reversing & Cheat Engine' 카테고리의 다른 글
27. CRC, 데이터 변조 검사 (0) | 2024.03.12 |
---|---|
26. 재유행 하는 메운디 주작 해보자 (0) | 2024.03.11 |
24. 간단한 게임 치트 도전 - 호출 (5) | 2024.03.09 |
23. 간단한 게임 치트 도전 - 좌표 장난 (1) | 2024.03.08 |
22. 간단한 게임 치트 도전 - 밝기 조절 (0) | 2024.03.07 |