BlackJackson의 Hacking Story

HappyBunny님의 리버싱 기초강좌 - 패킹과 언패킹 본문

해킹/Reverse Engineering

HappyBunny님의 리버싱 기초강좌 - 패킹과 언패킹

BlackJackson 2019. 1. 29. 01:57

안녕하세요~ 이번 강좌는 HappyBunny님의 리버싱 기초강좌를 필자인 BlackJackson이 포스팅에 알맞게 수정해서 올리는 글입니다!


먼저 리버스 엔지니어링이란?



먼저 리버싱, 즉 리버스 엔지니어링은 무엇일까요? 영어로는 reverse engineering, 한국어로 번역하면 역공학이 됩니다. 간단히 말해서 프로그램의 코드를 역분석하여 프로그램내의 내부구조를 볼수있게 하거나 원하는 기능등을 만들거나 제거할 수 있도록 하는 것입니다!



언패킹에 앞서 패킹에 대해 알아봅시다!



패킹이란 프로그램의 코드를 압축시키는 것이라고 볼 수 있습니다. 패킹을 하는 이유로는 배포를 위해 프로그램의 크기를 줄이는 것과 프로그램의 코드, 즉 소스코드를 보호하거나 코드분석을 이용한 프로그램 분석을 막는 것이 있습니다. 패킹은 Compressor과 Protector 이렇게 2가지로 나뉘게 됩니다. Compressor의 역할은 파일의 크기를 줄여 배포하기 쉽게 만들어줍니다. 즉 알집이나 7z같은 압축프로그램이라고 비유할 수 있겠습니다! Protector는 리버싱에서 가장 중요한 부분입니다. Protector는 프롤그램의 소스코드를 숨기는 목적으로 사용되며, 만약 Protector 기법으로 패킹을 하게되면 파일의 크기는 늘어나지만 안티리버싱 기술이 포함되어 분석을 어렵게 만들어줍니다. Protector의 예로는 Themida, Yoda, ASProtect, armadilo, .Net Reactor등이 있겠습니다.



패킹의 한 종류인 Compressor의 동작방식에 대해 알아봅시다!



Compressor로 패킹된 프로그램은 어떻게 동작할까요? 그 해답은 다음과 같습니다. 패킹된 프로그램은 실행이 되며 언패킹 루틴을 통해 압축된것을 풀게 됩니다. 압축이 풀리게 되면 원본코드가 나오겠죠? 그 원본코드가 정상동작하는 구조입니다. 즉 원본코드가 기본적으로 가지고있는환경 메모리구조 또는 API를 그대로 맞춰줘야 합니다. 그래서 패킹을 할때는 원본코드의 주요 데이터만 간추려서 특정 세션에 인코딩하여 저장을하게 됩니다. 그리고 다른 세션에는 언패킹 루틴 코드를 저장하게 됩니다. 엔트리포인트는 언패킹 루틴 코드가 있는 곳을 가리키게 됩니다. 만약 프로그램이 실행되면 언패킹루틴을 먼저 돌며 패킹된 데이터를 언패킹하며 원본의 환경과 최대한 동일한 환경을 갖추기 위해 코드가 있던 세션, 데이터가 있던 세션, API가 있던 세션에 맞춰 디코딩을 하며 저장을 하게됩니다. 그러나 패킹을 해주는 Packer마다 패턴이 달라 모든 Packer가 이 패턴을 사용한다고 하기는 어렵습니다. 언패킹이 끝난 후에는 원본코드를 동작시켜야 하겠죠? 그러나 원본코드 또한 먼저 실행해야 하는 엔트리포인트를 가지고 있기 때문에 언패킹루틴이 마지막으로 원본코드의 엔트리포인트(OEP, Original Entry Point)로 분기시켜 원본코드가 정상적으로 동작하게 합니다. 물론 패킹된 파일이 가지고 있는 PE헤더의 내용 또는 여러 정보들은 패킹된 파일의 정보이며 원본파일의 정보는 아닙니다. 또한 언패킹이 끝나도 헤더의 값은 원본코드의 헤더정보로 바뀌지 않습니다. 헤더의 내용은 코더가 동작하고 있는 시점에서는 상관이 없기 때문입니다.



이제 언패킹을 알아봅시다!



언패킹에 앞서, 패킹이 동작하는 원리, 즉 패턴을 잘 알고있다면 언패킹을 하는 방법은 쉽게 떠오르는 법입니다. 패킹의 원리와 패턴을 공부한 지금, 언패킹을 하는 방법은 이해하기 쉬울것입니다. 패킹된 파일은 원본코드를 정상적으로 실행하기 위해 언패킹 루틴을 가지고 있고, 언패킹 루틴이 정상적으로 제 역할을 다하면 원본코드가 동작하게 됩니다. 여기서 언패킹루틴이 끝나고 원본파일의 엔트리포인트로 점핑을 해줘야 원본코드가 정상동작할 조건을 만들어 주는것이죠. 그렇기에 언패킹루틴이 마지막에 원본코드의 엔트리포인트로 점프를 해줍니다. 여기서 우리는 재미있는 사실을 알아낼 수 있습니다. OEP(Original Entry Point)로 점프하기 전에는 언패킹이 마무리가 되어야 합니다. 다른말로 만약 OEP로 순서가 달라지는 부분을 찾게 된다면, 메모리에 위치하는 언패킹된 데이터를 얻을 수 있겠죠? 여기서 언패킹된 데이터를 덤핑, 즉 복사하게 되면 원본코드를 얻을수 있습니다! 그러나, 여기서 문제가 발생하게 됩니다. 프로그램이 실행되면 메모리에 프로그램이 로드가 되며 IAT를 바인딩하는 작업을 수행하게 됩니다. 여기서! IAT과 바인딩에 대해 잠깐 설명을 해드리겠습니다. IAT는 Import Address Table의 약자로 프로그램에서 사용되는 라이브러리에서 어떤 함수를 사용하는지 또는 함수명 그리고 함수시작 주소등에 대한 정보를 가지고있는 Table입니다. 그리고 바인딩은 변수에 값을 할당하는 것이죠. 여기서 왜 문제가 되냐? 위에서 말씀드린 것처럼, 프로그램이 로드되며 메모리에 상주하게 되고, IAT를 바인딩하게 됩니다. 이상태의 메모리를 덤프하게 되기 때문에 추후에 덤프된 코드를 실행할 때 IAT를 바인딩 할 수 없게 됩니다. 즉 IAT가 고정값이 되어버리는 것이죠. 즉 IAT를 수정해줘야 하는 문제가 생깁니다.


오늘 강의는 여기서 마치겠습니다. 긴 강의 읽어주셔서 감사합니다~^^

Comments