나만의 메모노트

[리버싱 핵심원리] PE File Format (4) 본문

Security/Reversing

[리버싱 핵심원리] PE File Format (4)

sp3arm4n 2021. 8. 1. 04:08

IAT (Important Address Table)

  • 개념 : 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블
  • Windows OS의 핵심 개념인 Process, Memory, DLL 구조 등에 대한 내용이 함축되어 있음
  • DLL 로딩 방식 중 Implicit Linking에 대한 메커니즘을 제공하는 역할

디버거를 통해 IAT 확인 해보기

  • 대상 프로그램 : notepad.exe
    Kernel32.dll의 CreateFileW() 함수 호출
  • CALL [1001104] → notepad.exe에서 kernel32.dll의 CreateFileW를 호출하는 명령어
    • CALL 명령어를 통해 호출하는 주소는 IAT 메모리 영역 (.text 섹션의 메모리 영역)
    • 1001104 주소에 저장된 7C810CD9라는 값을 통해 CreateFileW를 호출 (직접 호출 X)
      • 7C810CD9라는 값이 해당 프로세스 메모리에 로딩된 kernel32.dll의 CreateFileW 함수의 주소
      • Windows API를 호출하는 일반적인 방식 중 하나
  • CALL 명령어를 사용하지 않고 IAT 영역을 사용하는 2가지 이유
    • 환경에 따라 달라지는 kernel32.dll의 버전과 CreateFileW 함수의 주소
      • 컴파일러의 역할 : 해당 함수의 실제 주소가 저장될 공간을 마련한 후 CALL 명령어를 작성 ☞ 모든 환경에서 해당 함수 호출을 보장하기 위함
      • 파일이 실행되는 순간 PE loader가 그 위치에 해당 함수의 주소를 입력
    • DLL Relocation
      • DLL이 ImageBase 값에 로딩되고, 또 다른 DLL도 같은 곳에 로딩을 시도하면 PE loader는 다른 비어 있는 메모리 공간을 탐색한 후 로딩한다. 이러한 이유는 실제 주소를 하드코딩 할 수 없으며, PE Header에서 주소를 나타낼 때 VA가 아닌 RVA를 써야하기 때문이다.
    • ImageBase에 의한 DLL과 EXE 차이
      • DLL : PE Header에 명시된 ImageBase에 로딩된다고 보장 할 수 없음
      • EXE : 자신만의 가상 메모리 공간을 가지므로 자신의 ImageBase에 정확히 로딩됨

DLL (Dynamic Linked Library)

  • 개념 : 여러 프로그램에서 동시에 사용할 수 있는 코드와 데이터를 포함한 라이브러리
  • 16bit DOS 환경
    • DLL 개념이 존재하지 않음 👉 Library만 존재
    • Multi-Tasking을 지원하는 Windows OS에 비해 비효율적
  • 32bit Windows 환경
    • 매우 많은 라이브러리 함수 사용 (process, memory, window, message 등)
    • 문제점
      • 여러 프로그램이 동시에 실행될 경우 모든 프로그램마다 매우 많은 라이브러리가 동일하게 포함되어 실행된다면 심각한 메모리 낭비를 불러옴. (디스크 공간마저 낭비)
    • 해결방안
      • DLL 개념 고안
        • 프로그램에 라이브러리 포함 X → 별도의 파일(DLL)로 구성 → 필요 시 사용
        • 한 번 로딩된 DLL의 코드, 리소스는 Memory Mapping 기술로 여러 Process에서 공유
        • 라이브러리 업데이트 시 해당 DLL 파일만 교체 👉 쉽고 편리함
  • 실제 DLL 로딩 방식
    • Implicit Linking (암시적 링크) : 프로그램 시작 시 함께 로딩, 프로그램 종료 시 메모리에서 해제
      • 대부분의 프로그램에서 사용
        • .dll과 .lib 파일이 모두 필요
      • 프로그램 실행 시 DLL 내에서 호출한 함수가 있는지 검사 👉 없으면 프로그램 죽음
      • 동적 링크된 참조가 있으면 프로그램이 실행될 때 해당 dll 파일을 프로세스의 주소 공간에 매핑 👉 dll이 없으면 프로세스 죽음
      • Dll에 대한 진입점 함수 (초기화나 종료)가 있으면 OS가 그 함수를 호출 👉 없으면 프로그램 죽음
      • Dll은 프로세스가 실행될 떄 한 번 로드되고 계속 프로세스 주소 공간에 남아 있음
      • Dllmain 함수에 오류가 있거나 해당 dll이 없으면 프로세스가 바로 죽음
    • Explicit Linking (명시적 링크) : 프로그램에서 사용되는 순간 로딩, 사용이 끝나면 메모리에서 해제
      • 실행 시점까지 무슨 dll을 로딩할 지 모를 경우 필요
        • 설정 파일에 따라 로딩될 dll이 변경된 경우
      • Dllmain 함수에 오류가 있거나 해당 dll이 없으면 경로를 재설정
      • 실행 속도를 높이거나 필요 없는 dll을 실행 도중 해제하기 위해 사용
      • 프로그램과 Import Library 사이의 연결이 필요 없음
      • Explicit Linking에 사용되는 3가지 함수
        • LoadLibrary
          • 프로세스 내 주소 공간에 dll을 할당하는 함수
          • 실패하면 NULL을 반환
          • DLL 파일의 usage count를 1씩 증가
        • GetPrcoAddress
          • Export된 심볼의 시작 주소를 얻어옴
        • FreeLibrary
          • DLL 파일의 usage count를 1씩 감소시키고, 0이 되면 파일 이미지를 매핑 해제

IMAGE_IMPORT_DESCRIPTOR

  • IMAGE_IMPORT_DESCRIPTOR 구조체IMAGE_IMPORT_DESCRIPTOR 구조체
    • 자신이 어떤 라이브러리를 Import하고 있는지 명시되어 있음
      • Import : library에게 함수를 제공받는 일
      • Export : library 입장에서 다른 PE 파일에게 함수를 제공하는 일
  • 일반적인 프로그램 내에서 여러 라이브러리를 Import하므로 라이브러리의 개수만큼 구조체의 배열이 존재 (해당 구조체는 NULL 구조체로 끝남)
  • 주요 멤버
    멤버명 의미
    OriginalFirstThunk INT(Import Name Table)의 주소(RVA)
    Name Library 이름 문자열의 주소(RVA)
    FirstThunk IAT(Import Address Table)의 주소(RVA)
  • 참고사항
    • PE Header에서 Table은 배열을 의미함
    • INT와 IAT는 long type (4바이트 자료형) 배열이고 NULL로 끝남 (크기가 따로 명시되어 있지 않음)
    • INT에서 각 원소의 값은 IMAGE_IMPORT_BY_NAME 구조체 포인터 (IAT도 같은 값을 가지는 경우 있음)
    • INT와 IAT의 크기는 같아야 함
  • IMAGE_IMPORT_DESCRIPTOR 구조notepad.exe의 kernel32.dll에 대한 IMAGE_IMPORT_DESCRIPTOR 구조
IAT 입력 순서

1.  IID의 Name 멤버를 읽어서 라이브러리의 이름 문자열("kernel32.dll") 획득
2.  해당 라이브러리를 로딩 → LoadLibrary("kernel32.dll")
3.  IID의 OriginalFirstThunk 멤버를 읽어서 INT 주소 획득
4.  INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA) 획득
5.  IMAGE_IMPORT_BY_NAME의 Hint(ordinal) 또는 Name 항목을 이용하여 해당 함수의 시작 주소 획득

     ➡ GetProcAddress("GetCurrentThreadId")
6.  IID의 FirstThunk(IAT) 멤버를 읽어서 IAT 주소 획득
7.  해당 IAT 배열 값에 위에서 구한 함수 주소 입력
8.  INT가 끝날 때까지(NULL을 만달 때까지) 위 4 ~ 7 과정 반복
9.  INT와 IAT의 각 원소가 동시에 같은 주소를 가리키고 있지만, 그렇지 않은 경우도 많음