01 리버싱을 위한 기초 지식

HnBrd·2023년 5월 22일
0

리버싱 입문

목록 보기
1/4
post-thumbnail

얼마전부터 특정 프로그램을 좀 딥하게 분석하고 싶다는 열망에 사로잡혀 있다가 덜컥 리버싱 입문이라는 책을 덜컥 구매해버렸다.
챕터 1을 읽고나서야 책에 대해 검색해봤고, 그 결과 알게 된 건 리버싱 입문용으로 아주 좋은 책이라는 것이었다. 실제로 리버싱에 대해 아무것도 몰라도 쉽게 따라할 수 있도록 "지금은 몰라도 돼"를 적재적소에 배치해놓아서 수많은 의문점이 나를 미궁으로 빠지게 하지 않도록 도와주었다.

책의 후반부를 읽고 있는 지금은 책의 핵심내용과 직접 따라하는 과정을 어딘가에 정리해놓으면 좋겠다는 생각에 벨로그를 시작하기에 이르렀다.

1. 리버싱을 위한 기초 지식

1.1. 리버싱을 위한 프로그램 실행 구조

  • 컴퓨터의 기본 구성 요소 : CPU / 메모리 / HDD

  • 윈도우에서는 실행 파일을 PE(Portable Executable) 파일이라고 한다

    • PE 파일의 구성
      • Header : 프로그램을 실행하는데 필요한 기본 정보와 파일을 메모리 어디에 저장할 것인가 배치 정보
      • Body : 코드 + 데이터
  • 메모리의 구성

    1. 코드 영역 : 프로그램 코드가 들어가는 영역
    2. 데이터 영역 : 정적 변수와 전역 변수 저장
    3. 스택 영역 : 함수 호출 시 사용되는 매개 변수와 지역 변수가 저장
    4. 힙 영역 : 동적 메모리 할당에 사용
  • PE를 실행하면 일어나는 일

    1. 운영체제에 있는 로더(Loader)가 PE헤더 정보를 분석해서 PE바디를 메모리에 배치
    2. 메모리에 로딩 될 때 코드 영역과 데이터 영역에 자료 들어감
    3. 프로그램이 실행되면서 스택과 힙 영역에 데이터 쌓임
  • PE 파일의 Entry Point에서 파일 실행이 시작된다

    • Entry point 정보는 헤더에 저장
  • 명령을 실행할 때 필요한 데이터는 모두 레지스터에 존재해야 함

    • 다른 프로그램에 CPU사용을 넘겨주는 작업
      • 레지스터를 메모리 영역으로 복사하고 사용할 데이터를 레지스터로 올리는 일련의 과정을 Context Switching이라고 한다

2. 레지스터

  • 레지스터는 CPU에서 사용하는 고속의 기억장치

  • 레지스터를 언제 사용하는가

    • 연산을 수행하기 위해 메모리에 있는 데이터를 저장
    • 연산 중간 데이터를 저장
  • 인텔 x86 CPU의 기본 구조는 IA-32 아키텍쳐

    • 9개의 범용 레지스터
      • EAX (Extended Accumulator Register)
        • 곱셈, 나눗셈 수행, 함수의 반환 값 저장
      • EBX (Extended Base Register)
        • ESI, EDI와 결합해 인덱스에 사용
      • ECX (Extended Counter Register)
        • 반복 명령어를 사용할 때 ECX에 횟수를 저장해놓고 수행
      • EDX (Extended Data Register)
        • EAX와 같이 사용, 부호 확장 명령에 활용
      • ESI (Extended Source Index)
        • 데이터 복사하거나 조작 시 소스 데이터 주소 저장
        • ex) ESI가 가리키는 주소의 데이터를 EDI가 가리키는 주소로 복사
      • EDI (Extended Destination Index)
        • 복사의 목적지 주소
      • EBP (Extended Base Pointer)
        • 하나의 스택 프레임의 시작 주소 저장
      • ESP (Extended Stack Pointer)
        • 하나의 스택 프레임의 끝 주소 저장
        • PUSH, POP 명령에 따라 4Byte 씩 변경
      • EIP ( Extended Instruction Pointer)
        • 다음에 실행할 명령어가 저장된 메모리 주소 저장
    • 레지스터들은 시스템에 따라서 잘게 쪼개서 사용이 가능하다

3. 스택과 스택프레임

3.1. 스택

  • 메모리의 한 부분
    • 시작 주소에서 주소가 작아지는 방향으로 데이터가 쌓인다
    • 현대 프로그램이 사용하는 스택의 위치는 ESP (=스택 포인터)에 저장
      • PUSH : ESP-4
      • POP : ESP+4
  • LIFO로 동작하는 자료구조
  • 스택의 역할
    • 서브루틴으로 인자를 전달
    • 서브루틴 내부에서 사용하는 지역변수가 저장되는 공간을 제공
    • 서브루틴이 종료될 때 되돌아갈 주소를 저장

3.2. 스택 프레임

  • 서브루틴이 가지는 자신만의 스택 영역
  • 서브루틴이 호출될 때 스택 프레임이 생성, 복귀 주소로 돌아갈 때 소멸
  • 서브루틴 호출 순서
    1. 서브루틴을 호출할 때 필요한 인자들을 먼저 스택에 PUSH
    2. 호출 직전에 다시 돌아올 주소인 복귀 주소를 스택에 PUSH
    3. 스택 프레임 시작
    4. 이전 루틴이 사용한 EBP 레지스터 내용을 백업하고 해당 위치를 스택프레임의 EBP로 설정
    5. EBP를 데이터 참조를 위한 기준 주소인 Frame Pointer로 사용

4. PE파일

4.1. PE 파일 기본 개념

  • 컴파일러 : 고급 언어 (Visual C++, Java)로 만들어진 소프트웨어는 기계어로 번역해서 운영체제가 이해할 수 있도록 하는 기능과 여러 기능을 갖춘 프로그램

  • PE파일 : 번역되어서 만들어진 기계어 파일은 .exe, .dll과 같은 확장자로 만들어짐

  • 컴파일러

  • PE 파일은 PE 포맷에 따라 만들어야 하고 리버서는 PE 포맷에 대해 잘 알아야 한다

  • 윈도우 PE 파일의 구조

    • PE Header
      • IMAGE_DOS_HEADER
      • MS_DOS Stub Program
      • IMAGE_NT_HEADERS
        • Signature : 4Byte
        • IMAGE_FILE_HEADER : 핵심적인 정보가 담긴 구조체
        • IMAGE_OPTIONAL_HEADER : 핵심 정보가 담긴 구조체
          • 16 * IMAGE_DATA_DIRECTORY 구조체가 따라온다
    • Section Header (PE Header로 통칭되기도 함) : 최소 1개 이상
    • Section Data : 최소 1개 이상
  • IMAGE_OPTIONAL_HEADER

    • Image Base
    • Address of Entry Point
    • Base of Code (=RVA)

4.2. 주소 지정 방법

  • 주소 : 내가 찾는 데이터가 어디에 위치해 있는지 알려주는 데이터

  • 주소 형식

    1. pFile : PE 파일 내부에서의 오프셋 값 (파일)
      • 물리적으로 하드디스크에 저장되었을 때 의미 있는 값
    2. RVA (Relative Virtual Address) : PE 파일이 메모리로 로드됐을 때 저장되는 상대 주소 (메모리)
    3. VA (Virtual Address) : 가상 메모리상에서 저장되는 실제 주소 (메모리)
      • RVA, VA는 메모리에 로드됐을 때 의미 있는 값
  • DLL (Dynamic Linking Library)

    • 프로그램에서 필요한 라이브러리를 사용 시점에 동적으로 연결해서 컴파일 시점에 필요한 모든 기능을 집어넣지 않아도 되도록 라이브러리 형태로 빌드
    • DLL을 사용하기 위해 IAT 정보가 포함

4.3 IAT

  • IAT (Import Address Table) : 어떤 라이브러리의 어떤 함수를 가져다 쓰는지 기록해놓은 정보
  • 로더는 PE 파일을 메모리로 로딩할 때 IAT관련 dependency를 해소

5. OllyDbg

5.1. OllyDbg란?

  • OllyDbg는 윈도우용 어셈블러를 분석할 수 있는 디버거
  • 64비트 stable 버전이 없으므로 난 처음부터 x32dbg와 x64dbg를 사용할 예정

5.2. OllyDbg 기본 설정

  • 디버깅을 하고 나면 디버깅 정보를 udd 파일에 저장
  • udd 파일이 저장될 디렉토리를 별도로 지정해서 파일을 정리하기 쉽게 하는게 좋다
  • 메모리 덤프와 분석 결과에 대한 그래프 표현은 외부 플러그인을 이용

6. 어셈블러 기초 지식

6.1. 어셈블러의 개요

  • S/W를 실행하는 주체는 OS
  • 운영체네는 비트만 이해할 수 있다 (0, 1의 조합으로 된 정보)
  • 컴파일러는 우리가 만드는 고급 언어를 컴퓨터가 이해할 수 있는 저급 언어로 번역
  • 어셈블러는 컴퓨터가 이해할 수 있는 연속적 비트로 구성된 기계어를 사람이 알아볼 수 있도록 변환
    • 기계어와 1 : 1로 매칭되는 언어
    • 일종의 매크로 모음
    • 단순한 명령어의 조합으로 구성

6.2. 어셈블러 기본 구조

2개의 인자를 사용하는 명령어

  • 어셈블러 명령어는 최대 3개까지 인자를 받아들일 수 있다
  • 대표적인 명령어 예시) ADD
    • ADD EAX, EBX (= EAX 레지스터 값에 EBX 레지스터 값을 더해서 EAX 레지스터에 저장)
    • 1:1로 매칭되는 기계어 = 01D8

1개의 인자를 사용하는 명령어

  • 대표적인 명령어 예시) INC ESI

    • ESI = ESI + 1 or ESI++
    • 1:1로 매칭되는 헥사코드 = 46
  • 대표적인 명령어 예시2) PUSH

    • 뒤에 오는 인자를 스택 맨 위에 입력
    • 1:1로 매칭되는 헥사코드 = 6A
      • PUSH 0 = 6A 00

인자가 없는 명령어

  • 대표적인 명령어 예시) RETN
    • 뒤에 하나의 인자를 입력받을 수도 있다
    • 없는 경우 : 스택의 맨 위에 있는 값을 EIP 레지스터에 저장하는 역할을 한다
      • EIP 레지스터는 다음에 실행할 명령어를 저장
      • = RETN 명령어를 사용하면 스택의 맨 위 주소에 있는 명령어 실행
    • 스택 프레임 구조에서 많이 사용
      • 루틴에서 서브루틴으로 이동할 때 복귀할 주소를 스택에 PUSH
      • 서브루틴은 동작을 모두 수행 후 마지막에 RETN

7. 리버싱에 필요한 도구

7.1. 리버싱 기초 예제 abex crackme

7.2. 리버싱의 바이블 Lena의 강의

7.3. PE 파일 분석 프로그램 PEView와 Detect It Easy

  • PE View
    • PE 구조를 볼 수 있는 UI 제공
  • Detect It Easy
    • PE 구조는 기본적으로 제공
    • 프로그램을 개발한 언어와 패킹 여부에 대한 정보 제공

7.4. 메모리 덤프를 위한 OllyDbg 플러그인 OllyDumpEx

  • OllyDumpEx
    • 언패킹된 프로그램을 파일로 저장하기 위해 사용하는 플러그인

7.5. 손상된 IAT를 복구하는 LoadPE

  • 손상된 IAT 파일을 복구하려면?
    • PE 헤더 정보를 분석
    • 사용하는 DLL과 함수에 대한 정보를 일일이 맞춰야
  • 손상 파일은 일정한 규칙이 있기 때문에 프로그램으로 복구 가능
profile
잡식

0개의 댓글