Reverse Engineering/Reversing 이론 설명

[리버스_0x01] 함께 공부하는 Reversing_2

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


- Tool: Ollydbg

  

1. Study of the target

문제를 해결하기 위해서는 먼저 어떤 문제가 있는지를 알아야 할 것이다.
정확한 문제파악을 위해 우리가 사용할 Ollydbg로 해당 파일을 불러와 실행(F9) 해 보겠다.

 

그림과 같은 메시지 창이 뜨는 것을 확인할 수 있다. 제품 수명이 끝나 새로운 라이센스를 구매하라는 내용이다.
제품 수명과 관계없이 계속 쓰고싶다면 어떻게 해야 할까?

 

우선, 프로그램 안에 있는 TEXT를 추출하여 전체적인 흐름을 다시 한번 짚어 보겠다.
[마우스오른쪽버튼클릭->Search for->All referenced text strings]

 

 
그림에서와 같이 프로그램에서 쓰이는 text들을 추출해 볼 수 있다.
프로그램을 처음 실행하였을 때 나타났던 메시지
"Evaluation period out of data. Purchase new licence"를 쉽게 찾아 볼 수 있다.
그리고 의미심장한 "keyfile is not vaild. sorry."라는 문구를 찾을 수 있는데,
이것으로 미루어보아 우리가 넘어야 할 산은 두개인것으로 나타난다.
마지막으로 "You really did it! Congratz!!!"라는 문구는
우리가 모든 문제를 해결 했을 때, 나타날 메시지임을 쉽게 짐작할 수 있다.

 

이로써 문제 프로그램에 대한 개괄적인 분석을 모두 끝냈다.
 
  1. 프로그램을 실행하면,  라이센스가 만료되었음을 알리는 메시지가 나타난다.
  2. 이 메시지를 처리하면, 두번째로 keyfile이 맞지 않다는 메시지가 나타날 것이다.
  3. 이 모든 문제를 해결하면 "You really did it! Congratz!!!"라는 메시지가 뜰 것이다.

 

그럼 지금부터 해당 파일을 분석하면서 하나씩 문제를 해결하도록 하겠다.

 

2. Searching the patches

프로그램을 재 시작합니다. [Ctrl + F2 or 상단 메뉴바에 있는 뒤로가기(<<) 버튼 클릭] 그리고 코드를 한줄씩 실행[F8]해가며 해당 라인은 어떤 일을 수행하는지 이해해보겠습니다.
- stepping over the code: F8, 코드를 한 줄씩 실행한다하여 Tracing(추적)이라고
                                   부르기도 한다.
- stepping into the code: F7, 코드 실행 중 함수가 나왔을 때,
                                   해당 함수 내부를 실행하는 연산이다.
code는 call 이나 jmp 등의 흐름제어구문이 나오지 않는 이상 위에서 아래로 순차적으로 수행됩니다. 
 
 
 
0040107B | JNZ SHORT reverseM.0040109A
 
이 부분을 유심히 보면 이 구문의 결과에 따라 첫번째 산을 넘을 것인지 아닌지 여부가 판가름이
난다는 것을 알 수 있다. 여기서 시작되는 화살표를 보면 어떤 처리가 이루어지면
00440109A로 흐름이 이동하여 만료메시지창을 띄우지 않을 것이다. 
 
- JNZ is Jump if Not Zero
- CMP의 결과로 Zero Flag 값이 1이면 점프하지 않고, 0이면 점프한다.
- Zero flag는 결과 값이 0일때, 1로 set 된다.
그렇다면 이 흐름을 결정할 CMP 구문을 한 번 보면,
 
00401078 | CMP EAX, -1             // EAX = FFFFFFFF
 
- CMP is compare A, B
- A와 B의 값을 비교하는데, A-B연산으로 이루어지므로 두 값이 같다면 결과가
  0이 될 것이고, 다르면 0이 아닌 값이 나올 것이다.
EAX에 들어있는 값 FFFFFFFF와 -1을 비교하는 구문이다.
이 상태로라면 두 값이 같으므로 JNZ는 작동하지 않을 것이다.
 
우리의 문제는 첫번째 에러메시지를 출력하지 않는 것이고, 그러면 이 JNZ가 작동하도록 만들어야 한다.
첫번째 문제의 해결은 두 가지 방법을 생각해 볼 수 있다.
 
  • CMP의 결과로 zero flag가 0으로 set 될 수 있도록 이 구문을 수정하는 것이다.
        수정하는 방법은 해당 구문을 더블클릭하면 된다. "CMP EAX, 1"로 수정해 보겠다.
  • CMP의 결과로 어떤 값이 나오던지 JNZ를 무조건 분기하도록 만들면 어떨까?
        이번에도 손쉽게 해당 구문을 더블클릭하여 JNZ를 JMP로 바꿔주기만 하면 된다.
 
확인하는 방법은 프로그램을 재시작하고, 위에 나온 것과 같이 assem 코드를 수정하고
F8로 프로그램 흐름을 따라가다 보면 이전과는 다르게 에러 메시지 부분을 뛰어넘는 것을 볼 수 있다.
 
그리고 우리가 예상한바와 같이 두번째 에러메시지를 호출하는 부분으로 넘어간다.
이 메시지부분을 뛰어넘고 우리가 원하는 최종 메시지를 출력하기 위해서는 어떻게 해야 할까?
가장 간단한 방법으로 전체 프로그램 흐름을 알아볼 때 처럼 TEXT들을 추출하여 본다.
그리고 우리가 원하는 최종 메시지를 더블클릭한다.
그러면 그 text가 쓰이는 구문으로 바로 이동이 가능하다.
 
해당 TEXT를 내포하고 있는 부분의 시작되는 지점을 보면 Opcode 부분에 ">"표시가 보일 것이다.
이 라인에 초점을 두면 빨간 줄이 생기는데, 이것은 분기문의 흐름을 나타내는 것으로,
프로그램의 흐름을 이해하는데 있어 중요하다.
어떤 곳에서 여기로 분기가 되어지는지 빨간 선을 거꾸로 따라 올라가보겠다.
 
004010D8 | JMP reverseM.00401205
 
이 구문에서 바로 분기되어짐을 알 수 있다. 그렇다면 이 분기문이 올바르게 수행되려면
또 어떤 난관들이 있을까?
 
00401095 | JMP reverseM.0040121D
 
다시 위로 돌아가 첫번째 문제를 통과한 이후부터 다시 살펴보겠다.
이 구문을 따라가면 RETN 즉, 프로그램이 종료되는 시점으로 직행함을 알 수 있다.
 
004010B2 | JMP SHORT reverseM.004010F7
004010BF | JL SHORT reverseM.004010F7
004010D6 | JL SHORT reverseM.004010F7
 
이 구문들이 모두 두번째 에러 메시지를 나타내는 곳으로 향함을 알 수 있다.
이 구문을 모두 수행하지 않게 만들면 문제는 간단히 해결 될 것이다.
 
마우스 오른쪽 버튼 클릭->Binary->Fill with NOP
 
해당 구문은 더이상 수행 되지 않는다.
 
 
3. Save and Run
 
모든 문제를 해결한 후 새로운 파일에 저장하고, 다시 실행해 봄으로써 문제해결을 확인하겠다.
 
 
 
마우스 오른쪽 버튼 클릭->copy to excutable->all modifications->copy all
이후 새창에서 역시 마우스 오른쪽 버튼 클릭->save file
새롭게 패치한 파일을 불러와 실행해본다.
 

위와같은 메시지 창이 뜬다면 이번 문제를 깨끗이 해결한것이다!