usrbin
컴퓨터 일기
usrbin
전체 방문자
오늘
어제

공지사항

  • whoami
  • 분류 전체보기 (127)
    • 깔짝할짝 (61)
    • 잡지식 (30)
    • Network (7)
      • Programming (3)
      • Study (4)
    • Mobile (13)
    • Reversing (5)
      • Win API (2)
      • 분석 (0)
    • Kernel (4)
      • linux (1)
      • Windows (3)
    • Programming (5)

블로그 메뉴

  • 홈
  • 방명록

인기 글

태그

  • forensics
  • Scapy
  • pcap
  • pwnable.kr
  • BOF
  • pwntools
  • Pwnable
  • qt
  • Reversing
  • xcz.kr
  • Digital Forensics
  • sql injection
  • x64dbg
  • Packet
  • Network Programming
  • ftz
  • Hive Ransomware
  • System
  • Android
  • network
  • Follina
  • suninatas
  • monitor mode
  • nethunter
  • pcapng
  • PWN
  • HEVD
  • System Hacking
  • HackCTF
  • libpcap

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
usrbin

컴퓨터 일기

PE Structure - IAT
Reversing

PE Structure - IAT

2021. 11. 30. 22:27

적고보니 이해가 좀 되는 것 같다.. 결론은 IMAGE_NT_HEADERS의 필드였던 OptionalHeader의 필드인 DataDirectory 배열의 첫번째 DataDirectory[1]이고, 이게 가리키는게 IMAGE_IMPORT_DESCRIPTOR의 배열인것이다. 그리고 IMAGE_IMPROT_DESCRIPTOR의 INT를 보고 실제 함수의 주소를 찾아서 IAT에 쓰는 과정이 dll의 주소를 메모리에 올리는 과정이다.

0. IAT

0-0 설명

Import Address Table

IAT를 이해하기 위한 사전 지식.. 32bit Windows 환경으로 넘어오면서 메모리나 디스크 등의 자원 활용의 낭비를 막기 위해 DLL이 생겨났다. DOS 시절의 프로그램들은 특정 함수를 사용하기 위해 해당 라이브러리의 바이너리를 직접 프로그램에 박아버리는 방식으로 사용했기에 새로운 구조의 OS가 생성되면서 호환성 문제도 생기고, 앞서 말한 자원의 낭비가 생긴다.

이런 문제를 해결하기 위해 Dynamic Linked Library가 생긴 것이다. 프로그램에 직접 삽입하는 것이 아니라, 메모리에 라이브러리를 올려놓으면 그 라이브러리를 사용하는 여러 프로세스들이 공유를 할 수 있는 것이다.

0-1 DLL 로딩 방식

  1. Explicit Linking
    • 사용하는 순간에 로딩하고, 사용이 끝난 후에는 메모리에서 해제
  2. Implicit Linkng
    • 프로그램이 시작될때 같이 메모리에 올라가고 프로그램이 종료될때 같이 해제됨

IAT는 Implict Linking과 관련이 있다.

내가 밑에 실습에 썼던 바이너리를 ollydbg에 올려서 확인한 내용이다.

보면 CALL을 하는데 DS:[&KERNEL32.GetCommandLineA]에 있는 값을 호출한다. 직접 호출하는것이 아니라.. 그래서 아래 자세한 내용을 보면 실제 함수를 호출할때 참조하는 주소는 0x407000이고, 그 주소가 가리키는 값이 실제 GetCommandLineA의 주소(0x761E1EE0)이다.

결론은 앞서 말한 환경 종속성을 피하고 범용적으로 사용이 가능하도록 하기 위해 함수를 호출할 때 위와 같이 테이블을 참조하여 사용하게 한것이다. 실제 함수의 주소는(IAT의 경우) 프로그램이 시작될 때 dll도 메모리에 로딩이 되니까 그 시점에 호출할 함수의 주소가 결정이 된다. 이때 앞의 예처럼 0x407000 위치의 값에 함수의 주소를 넣으면 되는 것이다.

이렇게 실행중에 동적으로 라이브러리 함수들의 주소를 쫓아가야하고 그렇기 때문에 IAT나 EAT가 필요한 것이다.

1. IMAGE_IMPORT_DISCRIPTOR

1-0. 구조체 설명

앞에 적은 것 처럼 dll 같은 경우 프로그램이 어떤 라이브러리를 사용할지(import 할지) 프로그램이 로딩이 되어야 주소를 알 수 있다. 어떤 라이브러리를 쓰는지가 IMAGE_IMPORT_DESCRIPTOR에 명시되어있다.

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD    Characteristics; 
        DWORD    OriginalFirstThunk;    // Import Name Table address (RVA)
    } DUMMYUNIONNAME;
    DWORD    TimeDateStamp;
    DWORD    ForwarderChain;
    DWORD    Name; // library name string address (RVA)
    DWORD    FirstThunk; // IAT address (RVA)
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;

라이브러리를 한개만 사용하는 경우는 거의 없기 때문에, import한 라이브러리 개수 만큼 위 구조체를 배열의 형태로 가지게 된다. INT 주소를 따라가서 확인할 수 있는 내용이 _IMAGE_IMPORT_BY_NAME 구조체를 따른다.

필드 의미
OriginalFirstThunk INT(Import Name Table) RVA 주소
Name 라이브러리 이름 문자열의 RVA 주소
FirstThunk IAT(Import Address Table) RVA 주소

1-1. IAT에 함수 주소를 저장하는 과정

IMAGE_IMPORT_DESCRIPTOR의 구조체에서 Name 필드를 통해 라이브러리 이름의 문자열을 얻는다.INT를 보고 IAT를 쓰게 되는 것이다.

이후 LoadLibrary등을 이용해서 해당 라이브러리를 로딩한다(PE 로더가 하는 일이며, 이때 dll이 메모리에 올라감)

IMAGE_IMPORT_DESCRIPTOR의 OriginalFirstThunk 필드에서 Import Name Table의 주소를 얻는다.

INT 배열 하나하나를 읽으면서 _IMAGE_IMPORT_BY_NAME의 RVA 주소를 얻을 수 있다. INT 배열 원소 하나가 가리키는게 _IMAGE_IMPORT_BY_NAME이다.

_IMAGE_IMPORT_BY_NAME의 앞 2byte는 Hint이고 그 뒤에가 Name이다(이건 위에 구조체에서 확인가능). 이 이름을 가지고 GetProcAddress 등을 이용해서 함수의 시작 주소를 얻는다.

이후 FirstThunk 필드를 읽어서 IAT"의" 주소를 얻고, 그 주소값이 가리키는 곳에 앞에서 찾은 함수 주소를 저장한다.

이렇게해서 IAT가 함수의 실제 주소를 가리키게 된다.(이게 중요한거임)

2. 실습(1-1 과정의 실습)

나는 눈에 보인 Easy Keygen.exe를 이용해서 확인해봤다.

붉은색이 _IMAGE_NT_HEADERS.Signature (4byte, \x50\x45\x00\x00)

초록색이 _IMAGE_NT_HEADERS.FileHeader (20byte - 직접 세어봄 ㅎ)

노란색과 초록색 사이의 빈곳이 _IMAGE_NT_HEADERS.OptionalHeader 이며, 노란색 부분이 _IMAGE_NT_HEADERS.OptionalHeader.DataDirectory 이다.

이제 저 DataDirectory가 앞서 썼던 글에 있는 DataDirectory[0]~DataDirectory[15] 에 해당하는 내용들이 들어있는 것이다. (보라색이 DataDirectory[1], 파란색이 DataDirectory[12])

2-0. DataDirectory

IAT 내용을 자세히 파악하기 위해서는 DataDirectory의 내용을 잘 봐야한다.

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;     // RVA of the data
    DWORD   Size;               // Size of the data
};

내가 보고있는 exe에는 DataDirectory[1], DataDirectory[12]에만 내용이 들어있는 것을 확인할 수 있다. 1번은 Import table 자체에 대한 주소와 크기, 12번은 Import address table의 주소와 크기를 나타낸다.

중요한건 DataDirectory[1]의 VirtualAddress 값이 실제 IMAGE_IMPORT_DESCRIPTOR 구조체의 시작 주소임

1번을 구조체 필드와 함께 보면,

VirtualAddress: 74c8 (RVA)

Size: 28 byte

임을 알 수 있다.

.rdata의 VirtualAddress = 0x7000(RVA) >> 즉 DataDirectory[1]은 .rdata 영역에 있음

.rdata의 PointerToRawData = 0x7000

파일 offset = 74c8 - 7000 + 7000 = 0x74c8

크기는 28byte라고 했으니까 IMAGE_IMPORT_DESCRIPTOR 2개짜리 배열인 것으로 생각하면 될 것 같다.

2-1. IMPORT Directory Table

IMAGE_IMPORT_DESCRIPTOR 필드 RVA RAW
OriginalFirstThunk(INT) 74F0 74F0
TimeDateStamp 00000000  
ForwarderChain 00000000  
Name 789E 789E
FirstThunk(IAT) 7000 7000

위 RVA 영역은 위에서 확인했듯이 .rdata의 영역이다.

  1. OriginalFirstThunk(INT)
    • INT는 함수에 대한 정보가 있는 구조체에 대한 포인터 배열이다. 해당 정보를 통해 프로세스가 메모리에 로딩되었을 때 사용하려는 함수가 어느 주소에 있는건지 정확히 구할 수 있게 된다.
    • 75A4를 따라가보면 함수명을 찾을 수 있다. INT에서 따라가서 확인하게되는 정보가 IMAGE_IMPORT_BY_NAME 구조체이다.
    • 즉 앞 2byte 0108은 Hint(Ordinal) 값이고, 그 뒤에 NULL이 나오기 전까지가 함수명이다.
  2. Name
    • 앞서 봤듯이 Name 필드는 라이브러리 이름이 저장된 포인터(주소값)을 가진다. 즉 789E 위치에 라이브러리 이름이 저장되어있을 것이다.
    • 해당 위치에 가보면 KERNEL32.dll 이라는 문자열을 확인할 수 있다
  3. FirstThunk(IAT: Import Address Table)
    • 7000으로 가보면 위와 같은 숫자들로 채워져 있고, INT처럼 IAT도 포인터 배열 형태이다. 그래서 75A4, 75B6 등을 따라가보았다.
    • 0x75A4
      INT에서 따라갔던 것과 동일한 함수로 따라가게 된다.
저작자표시 비영리 변경금지 (새창열림)

'Reversing' 카테고리의 다른 글

PE Structure - EAT  (0) 2021.12.04
PE Structure - Header  (0) 2021.11.28
    usrbin
    usrbin
    컴퓨터 할거야

    티스토리툴바