ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 13 PE File Format(4) - IAT
    리버싱 엔지니어링/리버싱 핵심 원리 2022. 3. 23. 20:53

     IAT 

    : 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블 

    • Windows 운영체제의 핵심 개념인 process, memmory, DLL 구조 등에 대한 내용이 함축

     

     DLL  

    : 동적 연결 라이브러리(Dynamic Linked Library)

     

    동적 연결 라이브러리라 불리는 이유?

    • 16비트 DOS 시절, C 라이브러리에서 함수의 binary 코드를 그대로 프로그램에 삽입
    • Windows OS에서 멀티 태스킹을 지원하여 16비트 DOS 시절 방식이 비효율적
    • 따라서, 새로운 방법 고안
      • 프로그램에 라이브러리를 포함시키지 말고 별도의 파일(DLL)로 구성하여 필요할 때마다 불러 쓰자.
      • 한 번 로딩된 DLL 코드, 리소스는 Memory Mapping 기술로 여러 Process에서 공유해 쓰자.
      • 라이브러리가 업데이트되었을 때 해당 DLL 파일만 교체하면 돼서 쉽고 편하다. 

     

    • 실제 DLL 로딩 방식은 2가지
      • Explicit Linking : 프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제
      • Implicit Linking : 프로그램 시작할 때 같이 로딩되어 프로그램 종료할 때 메모리에서 해제
    • IAT는 Implicit Linking에 대한 메커니즘을 제공하는 역할

     

    OllyDbg를 통한 IAT 확인 : CreatFileW 호출 코드 

    • CreateFile 호출 시 직접 호출하지 않고 01001104 주소에 있는 값을 가져와 호출 (모든 API 호출 방식과 동일)
    • 01001104 주소는 '.text' 섹션의 메모리 영역 (더 정확히는 IAT 메모리 영역), 값은 768F3A90

    • 768F3A90 주소가 notepad.exe 프로세스 메모리에 로딩된 kernel32.dll의 CreateFileW 함수 주소

    (?) 그냥 CALL 768F3A90라고 하면 더 편하지 않을까?

    => 해당 방식이 16비트 DOS 시절 사용한 방식

    => 어떤 Windows, 언어, Service Pack에서 실행될지 알 수 없음. 환경에 따라 kernel32.dll 버전이 달라지고 함수의 주소가 달라짐. 따라서, 모든 환경에서 함수 호출을 보장하기 위해 실제 주소가 저장될 위치(ex. 01001104)를 준비 → 파일 실행 시 저장될 주소(ex. 01001104)에 함수 주소를 입력해줌.

    => DLL Relocation 때문에 실제 주소를 하드코딩할 수 없다. (VA가 아닌 RVA를 사용해야하는 이유도 됨)

          ex) a.dll을 ImageBase 값인 메모리 1000000에 로딩

          b.dll을 로딩하려니 a.dll이 존재 따라서 비어있는 메모리 공간(ex. 3E000000)을 찾아 로딩

     

     IMAGE_IMPORT_DESCRIPTOR  

    : 어떤 라이브러리를 import하고 있는지를 명시

    • IMAGE_IMPORT_DESCRIPTION 구조체
    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics;            
            DWORD   OriginalFirstThunk;       // INT(Import Name Table) address (RVA)
        };
        DWORD   TimeDateStamp;
        DWORD   ForwarderChain; 
        DWORD   Name;                         // library name string address (RVA)
        DWORD   FirstThunk;                   // IAT(Import Address Table) address (RVA)
    
    } IMAGE_IMPORT_DESCRIPTOR;
    
    typedef struct _IMAGE_IMPORT_BY_NAME {
        WORD    Hint;                         // ordinal
        BYTE    Name[1];                      // function name string
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
    • 라이브러리의 개수만큼 위 구조체의 배열 형식으로 존재, 마지막은 NULL 구조체로 끝남

    중요 멤버

    항목 의미
    OriginalFirstThunk INT(Import Name Table)의 주소(RVA) 
    Name Library 이름 문자열의 주소(RVA)
    FirstThunk IAT(Import Address Table)의 주소(RVA)

    참고)

    - PE 헤더에서 Table은 배열을 뜻함

    - INT와 IAT는 long type(4바이트 자료형) 배열, NULL로 끝남. 크기 따로 명시 X

    - INT에서 각 원소의 값은 IMAGE_IMPORT_BY NAME 구조체 포인터

    - INT와 IAT의 크기는 같아야함. 

     

    notepad.exe의 kernel32.dll에 대한 IMAGE_IMPORT_DESCRIPTION

    PE 로더가 임포트 함수 주소를 IAT에 입력하는 기본 순서

    1. IID(IMAGE_IMPORT_DESCRIPTION)의 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 항목을 이용해 해당 함수의 시작 주소를 얻는다.

         → GerProcAddress("GetCurrentThreadId")

    6. IID의 FirstThunk(IAT) 멤버를 읽어 IAT 주소를 얻는다.

    7. 해당 IAT 배열 값에 위에서 구한 함수주소를 입력한다. 

    8. INT가 끝날 때까지(NULL을 만날때 까지) 위 4~7과정을 반복한다. 

     

     

     실습  

    : notepad.exe를 이용한 실습

    • IMAGE_IMPORT_DESCRIPTION 위치 : PE 바디
      • PE 헤더의 IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress 값이 실제 배열 시작 주소(RVA)
    • IMAGE_IMPORT_DESCROPTIOM을 IMPORT Directory Table 이라고도 한다. 

    IMAGE_OPTIONAL_HEADER32.DataDirectory[1] 구조체 값

    • 첫 번째 4바이트가 VirtualAddress, 두 번째 4바이트가 Size 멤버
    • RVA of IMPORT Directory : 00007604
    • size of IMPORT Directory : 000000C8
    • RVA to RAW를 통한 File Offset 구하기

    File Offset(6A04) 확인

     

    IMAGE_IMPORT_DESCRIPTION 구조체 멤버별로 살펴보기

    File Offset Member RVA RAW
    6A04 OriginalFirstThunk(INT) 00007990 00006D90
    6A08 TimeDateStamp FFFFFFFF -
    6A0C ForwarderChain FFFFFFFF -
    6A10 Name 00007AAC 00006EAC
    6A14 FirstThunk(IAT) 00012C4 000006C4

    위 표를 참고하여 순서대로 진행

     

    1. 라이브러리 이름(Name)

    : Name 항목은 임포트 함수가 소속된 라이브러리 파일의 이름 문자열 포인터

     

    RVA : 7AAC → RAW : 6EAC 

    • "comdlg32.dll" 문자열 확인

     

     

    2. OriginalFirstThunk - INT

    : INT는 임포트하는 함수의 정보(Ordinal, Name)가 담긴 구조체 포인터 배열 → 프로세스에 로딩된 라이브러리에서 해당 함수의 시작 주소를 정확히 구할 수 있음

     

    RVA : 7990 → RAW : 6D90 

    • 주소 배열 형태 (배열의 끝은 NULL)
    • 주소 값 하나하나가 각각의 IMAGE_IMPORT_BY_NAME 구조체를 가리킴
    • 배열의 첫번째인 7A7A(RVA)를 따라가보자

     

     

    3. IMAGE_IMPORT_BY_NAME

     

    RVA : 7A7A → RAW : 6E7A 

    • 파일 옵셋의 최초 2바이트 값(000F)는 Ordinal, 함수 고유번호
    • Ordinal 뒤로 'PageSetUpDlgW' 함수 이름 문자열 확인 (마지막은 '\0' : Terminating NULL)

     

    4. FirstThunk - IAT(Import Address Table)

     

    RVA : 12C4 → RAW : 6C4  

    • 파일 옵셋 6C4~6EB 영역 : 'comdlg32.dll' 라이브러리에 해당하는 IAT 배열 영역
    • 배열은 NULL로 끝남
    • IAT 첫 번째 원소 값은 76324906으로 하드코딩, 메모리에 로딩될 때 정확한 주소 값으로 대체됨

     

    댓글

Designed by Tistory.