물마중

[리버스_0x03] 함께 공부하는 Reversing_4 본문

Reverse Engineering/Reversing 이론 설명

[리버스_0x03] 함께 공부하는 Reversing_4

zweistar2 2011. 1. 12. 14:15
작성자: Dear. Tom
편집자: 엔시스(sis@sis.pe.kr)
출   처: 보안인닷컴 팀 블로그[http://boanin.tistory.com]



1. Abstract
 
이번에는 RegisterMe라는 프로그램을 분석할 것이다.
이 프로그램을 실행시켜보면 2개의 오류 메시지가 뜨는데, 프로그램을 처음 실행할때와 종료할 때이다.
만약, 이 프로그램이 정상적으로 등록되어 있다면 이 오류 메시지는 나타나지 않을 것이다.
3장을 공부하기에 앞서 이전 내용들을 아직 보지 않으신 분들에게는 앞장에서 소개해서 대충 넘어가는 부분들이
있기때문에 먼저 보고 올것을 당부하면서, 오늘 내용을 시작해 보겠다.

 


2. Tools and target

- Tool: OllyDbg
- Target: Lanas Reversing for Newbies 03 에 포함된 RegisterMe
 


3. Study of the target
 
* 목표에 대한 사전 분석작업은 어떻게 작업을 시작해야 할지에 대한 자세한 정보를 제공해 주기 때문에 매우 중요하다.
 
먼저, 프로그램을 Olly로 불러와 실행시켜 보자.

 
 
 


메인창을 제외하고 2번의 성가신 메시지들이 우리를 따라다니는 것을 확인할 수 있다.
이것들을 어떻게 제어하면 좋을까? 프로그램을 재시작해서, 한줄씩 좀 더 자세히 분석해보자.



4. Finding the patches
 
필자를 포함하여, 리버싱을 처음 시작하는 사람들은 대개 Text Strings를 검색하는 것으로 모든 문제를 찾을 수 있다고 믿는다. 하지만, 소스코드가 패킹되어 있거나 암호화 되어 있다면 우리는 아무런 결과도 얻지 못할 것이다. 그래서 이번에는 텍스트를 추출하지 않고 분석해 보겠다.


이제 첫 줄부터 한 줄씩 실행(F8) 해보자!
 
 
00401002 | CALL <JMP.&KERNEL32.GetModuleHandleA>
 
두 번째줄 부터 심상치 않은 것이 나온다. GetModuleHandleA는 어떤 역할을 수행할까?
API를 참조하여 보니, 프로그램의 ImageBase 값을 반환한다고 한다.
 
그렇다면, ImageBase는 무엇일까?
 
ImageBase는 PE 파일이 메모리에 매핑될 시작 주소이다. RVA의 기준이 되는 주소이다.
보통 exe 파일의 경우에는 0x00400000의 값을 가지고, dll은 0x10000000의 값을 가지지만 꼭 항상 그런것은 아니다.
 

 
 
0040100C | CMP EAX, 0

앞의 그림에서 확인할 수 있듯이 EAX에는 400000값이 들어가 있으므로, 이 비교구문에서의 결과는 0이 아닐 것이다.

CMP A, B는 내부적으로 A-B 연산을 해서 판단한다는 것을 다들 아시죠? ^^

 
0040100F | JE SHORT Register.00401024
 
앞에서 비교한 결과가 0이 아니므로, 우리는 여기서 점프할 수 없을 것이다.
그리고 나면 우리는 오류메시지가 뜨는것을 확인 할 수 있다.
 
 

어떻게 하면 이 오류 메시지가 뜨는 것을 막을 수 있을까?
 
 
 
우선 우리는 간단한 해결책으로 JE 구문에서 Zero Flag 값을 1로 설정하는 것을 생각해 볼 수 있다.
그리고 계속 실행해보면 무사히 오류 메시지를 건너 뛰는 것을 확인 할 수 있을 것이다.
이렇게 우리는 임시방책을 적용해 봤지만, 매번 이 작업을 해줄 순 없을 것이다.
우리에겐 한번의 설정만으로 지속적으로 이 문제를 해결할 방법이 필요한 것이다!
 
계속 실행해보면, 이제 드디어 메인창이 뜨는 것을 볼 수 있다.
그리고 종료버튼을 누르면, 어김없이 2번째 오류 메시지가 나타난다.
 
 
 
그런데 코드를 보면 알겠지만, Jump구문처럼 이 오류 메시지 부분을 비켜갈만한 구문이 보이지 않는다.
우리는 어떻게 해야 할까? 
 
그리고 아래 구문을 실행하면 프로그램이 종료된다.
지금까지 알아봤던 문제를 해결하기 위해 재시작을 해보자!

앞으로도 항상 오류메시지를 뜨지 않게 하려면 어떻게 해야 할까?
사실, 이것은 간단하고도 다양한 해결방법이 존재한다.
 
먼저, 첫 번째 오류 메시지를 피하기 위한 해결책은 다음과 같다.
 
  • NOP로 오류 메시지 처리 부분을 원천봉쇄한다.
  • owner handle 구문을 변경한다.
0040102c | PUSH 0
 
메시지 박스는 owner window를 가지지 않기 때문에, 1을 넣어 owner window를 만들어 버린다면, 메시지 박스는 더이상 보이지 않을 것이다. 왜냐하면 owner window는 실제로 존재하지 않기 때문이다.
  • EP(Entry Point)를 변경한다.
프로그램은 실행 될 때, main이나 WinMain, 혹은 DLLMain에서 시작하는데, 이 위치가 RVA로 저장되어 있다.
바로 이 위치를 EP(Entry Point)라고 한다. 

그렇다면 RVA는 또 뭘까?
 
RVA란,
    Relative Virtual Address의 약자로서 Offset과 같은 개념이라고 봐도 무방하다. 
 
VA란,
    Virtual Address의 약자로써, 실제 물리적 메모리 주소에 매핑한 가상 메모리 주소를 말한다.
그리고 이 해결책을 이해하기 위해서는 PE 구조에 대한 이해가 필요하다. 하지만, PE 구조를 모두 설명하기에는 엄청난 지면의 요구가 필요하고, 청정산소님께서 먼저 올려주신(혹은 앞으로 올려주실) 훌륭한 자료가 있기 때문에, 여기서는 간단히 소개만 하고 자세한 내용은 청정산소님의 포스팅 자료로 대신하고자 한다.
 
 관련글 : 악성코드 분석을 위한 UnPacking과 Windows PE 구조

 
 
5. The PE File Structure
 
  • PE 포맷은 32bit 혹은, 64bit 윈도우즈에서 사용되는 실행파일, 오브젝트 코드, DLLs의 파일 포맷이다.
  • PE data 구조
DOS header
DOS stub
PE File Header
Image Optaional Header
Section Table
Data Directories
Sections
  • PE header는 [imagebase]에서 [imagebase + 1000]에 위치한다.
  • 우리의 경우에는, 400000에서 부터 401000까지 해당한다.
  • RegisterMe의 ImageBase는 400000부터 시작한다는 것을 상기해보자.
 
 
 
마우스 우클릭 -> go to -> Expression 혹은 간단히 ctrl+G를 입력한다.
 
 
 
그리고 그림과 같이 ImageBase 값인, 400000를 입력한다.
 
 
 
위 그림에서 선택된 부분이 바로 DOS MZ Header에 해당한다.
그리고 바로 옆 ASCII로 변환된 부분을 보면 알겠지만, 첫 두 바이트가 MZ를 의미한다.
 
 
 
이제 상단 메뉴바에서 M을 선택하여 Memory Map을 살펴보자.
 
 
 
00400000 | PE header 를 더블클릭 해보면, 그림에서와 같이 Dump 창이 뜬다.
 
 
 
 
덤프창의 페이지를 조금 내리면, PE 헤더 포인터가 보인다.
이것은 PE 파일 헤더의 시작을 가리키는 포인트로, 보이는 바와 같이 헤더의 시작은 004000C0이다.
 
그런데 어떻게 C0 00 00 0000 40 00 C0인지 궁금하지 않은가?
 
첫째로, 바로 Endian 방식의 차이때문인데, 우리가 흔히 쓰는 X86 계열은 모두 Little Endian 방식을 쓴다.
이 방식은 바이트 단위로 보이는 것을 거꾸로 읽으면 된다.
 
둘째로, 이 프로그램의 시작지점이 400000 인 것을 우리는 알고 있다. Windows loader는 메모리에서 PE header와 maps를 찾기 위해 자동으로 오프셋에 ImageBase 값을 더한다.
 
그래서 C0 00 00 00 == 00 40 00 C0 과 같이 기이한 수식이 성립할 수 있는 것이다.
 
 
 
이제 덤프창에서 AddressOfEntryPoint = 1000 을 찾아보자.
우리는 첫번째 오류 메시지를 피하기 위해 프로그램이 시작되는 지점을 수정할 것이므로,
Memory Map 창에서 PE 덤프를 하여 나타난 OEP 주소지(004000E8)로 Hex dump 창에서 이동해 보자.
 
 
 
해당 주소지의 첫 번째 값들 00 10 00 001024로 바꾼다면 어떻게 될까?
그러면 EP값이 변경되어, 성가신 오류 메시지를 효과적으로 피할 수 있을 것이다.
 
 
 
00 10 00 00 부분을 선택한 다음, 마우스 우클릭 -> Binary -> Edit 선택 혹은 Ctrl + E를 누른다.
그리고 24 10 00 00 을 입력한다.
 
1024로 바꾸어야 하는데 왜 24 10 00 00을 입력하는지 모두들 이해했으리라 생각하지만,
한번더 짚고 넘어가기 위해 다시 한번 설명해 보겠다.
 
우리가 사용하는 X86 계열은 모두 Little Endian방식을 이용하는데, 이 방식의 특징은 실제 값을 바이트 단위로 거꾸로 저장한다는 것이다. 16진수 2개가 하나의 바이트를 이룬다는 것도 알고 있을 것이다.
그래서 우리가 입력하려는 값 00001024는 24 10 00 00으로 적어주어야 한다. 
 
 
 
6. Making the patches
 
우리가 패치한 새로운 파일을 저장하기 위해서,
마우스 우클릭 -> Copy to Excutable file -> Save File(다른 이름으로 저장) 을 한다.
 
그리고 새로 저장한 파일을 열어보자!
EP가 바뀌었기 때문에, 무난히 첫번째 오류 메시지를 피할 수 있다.
 
그렇다면 2번째 오류 메시지는 어떻게 처리해야할까?
 
 
 
간단히 오류 메시지 처리부분을 모두 NOP로 만들어(우클릭 -> Binary -> Fill with NOPs), 실행되는 것을 막는다.
그리고 저장(우클릭 -> Copy to excutable file -> All modification, Copy All, Save file)
(첫번째 오류 메시지 처리에 비해선 많이 쉽죠? ^^;)
 
 
 

7. Testing the RegisterMe
 
숨가쁘게 달려왔는데, 이제 마지막으로 확인해 볼 일만 남았다. 
새로 패치판 파일을 불러와서 실행(F9) 해보자!
 

 
더 이상 메인창 이외에 다른 성가신 메시지들이 뜨지 않는 것을 확인할 수 있다!!