본문 바로가기
Security/Reversing

[Dreamhack Reverse Engineering] STAGE 5

by 단월໒꒱ 2022. 5. 10.

 Tools: IDA

 

  IDA(The Interactive Disassembler)는 Hex-Rays 사에서 제작한 디스어셈블러이다.

 

  IDA의 메인 기능은 disassemble이지만 이 외에도 여러 환경에서의 디버깅과 다양한 아키텍처 디컴일과 같이 여러가지 기능을 제공한다.

  이 때문에 바이너리를 분석해야 하는 리버스 엔지니어링 업무에는 대부분 IDA를 사용한다.

 

  리버싱을 처음 공부하는 개인이 사용하기 적합한 IDA Freeware는 x64 아키텍처에 한해 디컴파일 기능을 지원하며, 상업적 목적 이외의 교육적 목적으로만 사용 가능하다.

 

 

 

IDA  설치

 

   IDA Freeware는 https://hex-rays.com/ida-free/ 에서 다운로드할 수 있다.

 

 

 

 

   설치가 완료된 후 IDA를 실행시키면 위와 같은 창이 뜬다.

 

 

 

파일 열기

 

   IDA에 파일을 로드하기 위한 방법으로는 두가지가 있다.

     1) File/Open/파일 선택

     2) 파일을 드래그&드롭

 

 

   파일을 로드해보자.

 

 

 

 

   IDA 파일 로딩

 

 

 

 

   정상적으로 파일이 열린 모습

 

 

 

IDA 둘러보기

 

  1. Functions window

 

 

 

 

   IDA에서 분석한 프로그램의 함수를 나열한다.

   해당 창에서 Cmd + F 단축키로 원하는 함수를 찾을 수 있다.

 

 

  2. Graph overview

 

 

 

 

   함수를 그래프화하여 보여준다.

   해당 창을 통해 함수의 흐름을 파악할 수 있다.

 

 

  3. Output window

 

 

 

 

   분석 과정을 메세지로 출력한다.

   해당 창을 통해 IDA의 분석 과정을 알 수 있다.

 

 

  4. View

 

 

 

 

   디컴파일 결과, Hex-View, 구조체 목록 등의 화면을 표시한다.

 

 

 

IDA  기능

 

  1. 임의 주소 및 레이블 이동

 

 

 

 

   단축키 G를 사용하여 임의 주소 또는 레이블로 이동할 수 있다.

 

 

  2. 함수 및 변수 이름 재설정

 

 

 

 

 

 

   단축키 N을 사용하여 함수 및 변수 이름을 재설정할 수 있다.

   정의되지 않은 함수 및 변수의 경우 해당 기능을 통해 이름을 설정하여 분석 속도를 향상시킬 수 있다.

 

 

  3. Cross reference(Xref)

 

 

 

 

   임의의 함수 또는 변수를 클릭하고, 단축키 X를 사용하면 해당 함수 및 변수가 사용되는 영역을 재참조할 수 있다.

 

 

  4. 함수 및 변수 타입 변경

 

 

 

 

   임의의 함수 또는 변수를 클릭하고, 단축키 Y를 사용하면 해당 함수 및 변수의 타입을 지정할 수 있다.

 

   함수의 경우, 전달되는 매개 변수를 추가하거나, 타입을 변경할 수 있다.

 

 

  5. Strings

 

 

 

 

   단축키 Shift + F12를 사용하여 바이너리에서 사용하는 모든 문자열을 조회할 수 있다.

 

   함수의 심볼이 존재하지 않거나, 복잡할 경우 문자열을 통해 분석 시간을 크게 단축할 수 있다.

 

 

  6. Decompile

 

 

 

 

   IDA에서 제공하는 가장 강력한 기능으로, 어셈블리를 C언어 형태로 변환하여 보여준다.

 

 

 

 

 


 

 

 

 Exercise: Helloworld

 

  HelloWorld.exe를 분석해보는 실습을 할 예정

 

 

/*
    File: hello-world.cpp
    Build opts:
      - /MT -> Library Static Linking
      - /DYNAMICBASE:NO -> Disable ASLR
      - /od -> Disable Optimization
*/
#include <Windows.h>
#include <stdio.h>
char* str;
int main() {
  int delay = 1000;
  Sleep(delay);  // 1000ms(1초)를 대기합니다.
  str = (char*)"Hello, world!\n";
  printf(str);
  return 0;
}

 

 

위의 소스코드를 참고해도 됨

 

 

 

정적 분석

 

  1. 파일 열기

    신뢰할 수 없는 프로그램을 분석할 때는 악성 프로그램일 가능성을 대비하여 정적 분석을 먼저 시도해보는 것이 바람직

    

    파일이 정상적으로 열리면 아래와 같은 창이 뜨는데, 바로 OK를 누른다.

 

 

 

 

 2. main 함수 찾기

   정적 분석은 주로 main 함수를 찾고, 이를 분석하면서 시작된다.

 

   바이너리에서 어떤 함수를 찾는 방법에는 크게 두가지가 있다.

     1) 프로그램의 시작 지점인 진입점(Entry Point)부터 분석을 시작하여 원하는 함수를 찾을 때까지 탐색하는 방법

          - 바이너리의 규모가 조금만 커져도 분석에 소요되는 시간이 급증하므로 일반적으로 적용하기 어렵다.

     2) 대상 함수의 특성이나 프로그램의 여러 외적인 정보를 이용하여 탐색하는 방법

 

   여기서는 두번재 방법으로 main 함수를 탐색해보겠다.

 

 

  3. 문자열 검색

   프로그램을 정적 분석할 때, 많이 사용되는 정보 중 하나가 프로그램에 포함된 문자열이다.

 

   프로그래머는 디버깅 메세지를 출력하거나, 로그 파일을 생성하는 등의 목적으로 여러 문자열을 프로그램에 포함시키는데, 이 문자열들은 특성상 유용한 정보를 제공할 때가 많다. 예를 들어, 프로그램의 로그와 관련된 문자열은 로그를 생성하는 함수의 이름과 인자가 적혀 있기도 한다. 이 문자열이 어느 함수에서 사용되는지 찾는다면, 원하는 함수를 쉽게 찾을 수도 있고, 함수의 이름과 인자를 통해 기능을 유추할 수도 있다.

 

   IDA는 바이너리에 포함된 문자열을 쉽게 찾을 수 있도록 "문자열 탐색" 기능을 제공하고 있다.

   

   아래는 이미지에 대한 설명을 할 것이다.

 

 

   

 

   Shift + F12를 누르면, 위와 같이 바이너리에 포함된 문자열이 열거된 Strings 창이 나타난다. 

 

 

 

 

   많은 문자열 중에 'Hello, world!'라는 문자열이 보인다.

   컴파일 과정에서 다양한 문자열이 바이너리에 추가되는데, 이 문자열은 그런 종류가 아니라 프로그래머가 넣었을 것으로 추측할 수 있다.

 

 

 

 

   이 문자열을 더블 클릭하여 따라간다.

 

 

  4. 상호 참조

   정적 분석을 하다가 어떤 수상한 값이나 함수를 찾았다면, 상호 참조(Cross Reference, XRef) 기능을 통해 이를 참조하는 함수를 분석할 수 있다. 

 

   앞에서 찾은 "Hello, world!\n"라는 수상한 문자열이 어디서 사용되는지 추적해보자.

 

 

 

 

   "aHelloWorld"를 클릭하고 상호 참조 단축키 X를 누르면 위와 같이 xrefs(cross reference) 창이 뜬다.

   이 창에는 해당 변수를 참조하는 모든 주소가 출력된다.

 

   첫번째 항목을 더블 클릭하여 따라가면 main 함수를 찾을 수 있다.

 

 

 

 

  5. main 함수 분석   

   main 함수를 찾았으므로, F5를 눌러 이를 디컴파일한다.

 

 

 

 

   ① 인자 분석

       IDA는 argc, argv, envp로 3개의 인자를 받는다고 해석했다.

 

   ② 동작

       1) Sleep 함수를 호출하여 1초 대기한다.

       2) qword_14001DBE0에 "Hello, world!\n" 문자열의 주소를 넣는다.

       3) sub_140001060에 "Hello, world!\n"을 인자로 전달하여 호출한다.

       4) 0을 반환한다.

 

   ③ 반환 값

       0을 반환한다.

 

 

6. sub_140001060 함수 분석

   sub_140001060 함수는 va_start 함수를 통해 가변 인자를 처리하는 함수이다.

   __acrt_iob_func 함수는 스트림을 가져올 때 사용되는 함수인데, 인자로 들어가는 1은 stdout을 의미한다.

   따라서 문자열 인자를 받고 stdout 스트림을 내부적으로 사용하는 가변 함수임을 알 수 있다.

   이러한 모든 정황을 통해 sub_140001060 함수는 printf 함수로 추정할 수 있다.

 

 

 

 

   cf) 스트림 (Stream)

       데이터가 조금씩 흘러들어온다는 의미에서 명명되었다.

       데이터는 스트림의 형식으로 한 프로세스에서 다른 프로세스로, 또는 한 프로세스에서 다른 파일 등으로 이동한다.

       운영체제는 stdin, stdout, stderr와 같은 기본 스트림들을 프로세스마다 생성해주는데, 이들은 일반적으로 사용자와 프로세스를 연결해주기 위해 사용된다.

       위의 기본 스트림 외에, 프로그래머는 필요에 따라 스트림을 생성할 수도 있다. 

 

 

 

 

동적 분석

 

  main 함수 진입

  1. 중단점 설정(Break Point, F2) 및 실행(Run, F9)

   대부분의 디버거에는 우리가 원하는 지점까지 프로그램을 실행시킬 수 있는 중단점과 실행이라는 기능이 있다.

   중단점을 특정 주소에 설정하고, 실행 명령을 내리면 프로그램은 중단점까지 멈추지 않고 실행된다.

  

   이러한 기능을 이용하여 main 함수로 진입해보자.

 

 

 

 

   main 함수에 중단점을 설정해보자. 단축키는 F2이다.

 

 

 

 

   디버깅을 시작하여 main 함수까지 실행한다. 디버깅 시작 단축키는 F9이다. (하지만 mac에서 단축키가 안 먹힌다.)

   디버거를 고르라는 창이 나타나면 Local Windows debugger를 선택한다. (하지만 mac에서 창이 안 뜬다.)

 

 

 

 

   동적 분석을 위한 준비가 끝났다. (하지만 mac에선.....)

 

 

  2. 한 단계 실행(Step Over, F8)

   Step Over(F8)은 관심 있는 부분의 코드를 정밀하게 분석하기 위해 사용하는 기능이다.

 

   이 기능을 이용하여 프로그램의 동작을 분석해보자.

 

 

 

 

  1. sub rsp, 38을 통해 main 함수가 사용할 스택 영역을 확보한다.
  2. rsp+0x20에 4 바이트 값인 0x000003e8을 저장한다.
  3. rsp+0x20에 저장된 값을 ecx에 옮긴다. (이는 함수의 첫 번째 인자를 설정하는 것이다.)
  4. Sleep함수를 호출한다. ecx가 0x3e8이므로, Sleep(1000)이 실행되어 1초간 실행이 멈춘다.
  5. “Hello, world!\n” 문자열의 주소를 rax에 옮긴다.
  6. 아래의 메모리 덤프 창을 이용하여 0x14001a140의 데이터를 보면 실제로 해당 문자열이 저장되어 있음을 확인할 수 있다.
  7. rax의 값을 data세그먼트의 주소인 0x14001dbe0에 저장한다.
  8. 0x14001dbe0에 저장된 값을 rcx에 옮긴다. 이는 다음 호출할 함수의 첫번째 인자로 사용될 것이다.
  9. 0x140001060함수를 호출한다. 이전에 정적 분석을 통해 이 함수를 printf함수라고 추측했었다.

 

  3. 함수 내부로 진입하기(Step Into, F7)

   어떤 함수를 분석할 때, 그 함수가 호출하는 다른 함수까지 정밀하게 분석할 때, Step Into(F7) 기능을 사용한다.

 

   printf()에 중단점을 설정하고 해당 함수의 내부로 진입해보자.

 

 

 

 

   디버깅을 중단하고, printf를 호출하는 0x14000110b에 중단점을 설정한다.

 

 

 

 

   디버깅을 다시 시작하고, Continue하여 printf 함수에 도달한다.

 

 

 

 

   F7 단축키를 통해 함수 내부로 들어간다.

   함수 내부로 RIP가 이동한 것을 확인할 수 있다.

 

 

  4. Appendix, 실행 중인 프로세스 조작하기

   IDA를 이용하면 실행 중인 프로세스의 메모리를 조작할 수 있다.

 

   기존 코드에서는 Sleep(delay=1000)으로 1초 동안 프로세스를 정지시켰다.

   이번에는 delay의 값을 1000000으로 조작하여 1000초 동안 프로세스를 정지시켜보자.

 

 

 

 

   delay를 Sleep 함수의 인자로 전달하는 부분에 중단점을 설정하고, 프로세스를 재시작한다.

 

 

 

 

   스택을 보면 rsp+0x20에 delay값인 0x3E8이 저장되어 있는 걸 확인할 수 있다.

 

 

 

 

   해당 값을 클릭하고, F2를 누른 뒤 0xf4240(=1000000)을 입력한다.

   그리고 다시 F2를 눌러서 값을 저장하면, delay의 값이 변경된 것을 확인할 수 있다.

 

 

 

 

   F9를 눌러서 Sleep 함수를 호출하면, 이전과 달리 오랫동안 프로세스가 재개되지 않는다. (약 20분 동안 멈춰있을 것이다.)

 

 

 

 

댓글