본문 바로가기

0x00 /0x01 Reversing

- IAT (Import Address Table / PE File 관련)

# 이번 포스팅 내용은 "IAT(Import Address Table)" 입니다. 이전에 "RVA to RAW" 라고, PE 파일이 메모리상에 

   로딩된 주소인 RVA(프로세스 가상 메모리의 절대주소)를 RAW(File Offset)으로 바꾸는 연습을 했습니다. 

   이번 포스팅 주제인 IAT 내용을 잘 이해하시기 위해서는 RVA를 RAW로 바꾸는데 어려움이 없으셔야 합니다.

   PE File Format에서 사용되는 구조체에서는 대부분이 RVA로 주소값들을 명시하기 때문입니다.





# IAT (Import Address Table)


- 우선 IAT가 무엇인지 알아야겠죠? IAT는 "Import Address Table"의 약어입니다. 영단어 그대로 해석해보면 [ Import : (컴퓨터) (다른 프로그램

  에서 데이를) 불러오다 ] [ Address : 주소 ] [ Table : 테이블 ], "불러오는 주소 테이블" 정도로 생각해볼 수 있겠네요. 영단어 의미 그대로 

  붙이니까 대충 어떤것인지 느낌은 오는데, 뭔가 좀 이상하네요. 어떤건지 느낌이 오셨나요? 


  IAT(Import Address Table)는 프로그램에서 사용되는 라이브러리에서 어떠한 함수들을 사용하고 있는지, 함수명, 함수시작 주소 등에 대한 

  정보를 기술한 테이블입니다.




# IAT에 대해서 간단하게 설명했는데요. 이제 좀 더 자세하게 알아보도록 하겠습니다.




# IMAGE_IMPORT_DESCRIPTOR


- IMAGE_IMPORT_DESCRIPTOR 구조체는 PE 파일 자신이 어떤 라이브러리(DLL)를 임포트(Import)하고 있는지에 대한 정보들을 담고 있는 

  구조체입니다. 우선 IMAGE_IMPORT_DESCRIPTOR 구조체가 어떻게 생겨먹었는지 한번 구경해보고 알아보도록 하죠.


[ IMAGE_IMPORT_DESCRIPTOR 구조체와 IMAGE_IMPORT_BY_NAME 구조체 (출처:winnt.h) ]


- 위에 보이시는 것이 IMAGE_IMPORT_DESCRIPTOR 구조체 정의에 대한 내용입니다. 그리고 그 아래에 IMAGE_IMPORT_BY_NAME 구조체도 있

  는데요. IMAGE_IMPORT_BY_NAME 구조체는 IMAGE_IMPORT_DESCRIPTOR 구조체 멤버인 OriginalFirstThunk 멤버 값과 관련있습니다.

  조금 후 자세하게 설명하도록 하겠습니다.


- 우선 IMAGE_IMPORT_DESCRIPTOR 구조체에 대해서 얘기해보도록 하겠습니다. 위에서 말했듯이 이 구조체는 PE 파일에서 임포트해서 사용하

  는 라이브러리, 그리고 라이브러리에서 사용하고 있는 함수에 대한 정보를 담고 있는 구조체입니다. PE 파일은 여러 개의 라이브러리를 임포트

  하는 경우가 대부분입니다. 이러한 점을 때문에 IMAGE_IMPORT_DESCRIPTOR 구조체는 PE 파일에서 임포트하는 라이브러리 개수개만큼 배열 

  형식으로 되어 있으며, 구조체 배열의 마지막 구조체는 NULL 구조체로 구성되어 있습니다. 간단한 그림을 통해서 예를 들어보겠습니다.



- 이해가 가시죠? 이런식으로 임포트하는 라이브러리의 개수보다 1개 더 많은 수로 IMAGE_IMPORT_DESCRIPTOR 구조체가 배열 형식으로

  존재하게 됩니다.


- 이제 IMAGE_IMPORT_DESCRIPTOR 구조체의 멤버 변수에 대해서 알아보도록 하겠습니다.




# IMAGE_IMPORT_DESCRIPTOR 구조체의 멤버 변수


1. Characteristics 와 OriginalFirstThunk 멤버 변수

- 위 2개의 멤버 변수는 union 구조체로 정의되어있습니다. union 구조체는 멤버 변수들중 가장 큰 변수의 데이터형에 맞게끔 메모리가 할당되고,

  그 공간을 공유해서 쓰는 구조체입니다. 그런데 위에 선언된 두 멤버 변수 중 하나인 Characteristics 멤버 변수는 현재 사용되지 않는 멤버 변수

  입니다. OriginalFirstThunk 멤버 변수만 사용되는데, 이 멤버 변수는 INT(Import Name Table)의 RVA 값을 가집니다. INT(Import Name

  Table)은 IMAGE_THUNK_DATA32 구조체 배열로 구성되어 있는데요. 

 즉, OriginalFirstThunk 멤버 변수가 명시하는 값은 INT의 IMAGE_THUNK_DATA32 조체 배열의 주소(RVA)입니다. 



[ 출처 : 리버싱 핵심원리 ]



- IMAGE_THUNK_DATA32 구조체가 뜬금없이 나왔는데요. 이 구조체에 대해서도 알아보고 넘어가도록 하겠습니다.



[ IMAGE_THUNK_DATA32 구조체 (출처:winnt.h) ]


- 위에 보이시는 것이 IMAGE_THUNK_DATA32 구조체입니다. 이 구조체는 INT(Import Name Table), 그리고 IAT(Import Address Table)를 

 구성하는 구조체입니다. (각 4Byte 크기의 IMAGE_THUNK_DATA32 구조체들이 배열로 구성되어 있음) 


- IMAGE_THUNK_DATA32 구조체의 멤버 변수들은 union으로 선언되어 있는데요. 상황에 따라 사용하는 변수가 다르기 때문입니다. 그렇다면,

  각 멤버 변수들이 어떤 용도로 사용되는지에 대해서 알아보도록 하겠습니다.



- INT를 구성하는 IMAGE_THUNK_DATA32 구조체는 크게 2가지 용도로 사용되어 집니다.

  ( INT를 구성하는 IMAGE_THUNK_DATA32 구조체에서는 ForwarderString , Function 멤버 변수는 사용되지 않습니다 )


 ① AddressOfData 변수 : IMAGE_IMPORT_BY_NAME 구조체의 주소(RVA)를 명시할 때 사용하는 변수

    -  IMAGE_IMPORT_DESCRIPTOR 구조체를 설명하면서 IMAGE_IMPORT_BY_NAME 구조체에 대해서 잠깐 얘기를 했었죠? 

        IMAGE_IMPORT_DESCRIPTOR 구조체의 OriginalFirstThunk 멤버 변수와 관련이 있다고 했었습니다. 좀 더 정확하게 얘기한다면,

        OriginalFirstThunk 멤버 변수가 명시하고 있는 INT(Import Name Table)를 구성하고 있는 IMAGE_THUNK_DATA32 구조체의 값과

        관련이 있는 겁니다. 지금 IMAGE_THUNK_DATA32 구조체의 멤버 변수에 대해서 설명하고 있었는데, AddressOfData 변수를 사용

       하게 되면 바로 IMAGE_IMPORT_BY_NAME 구조체의 주소를 명시하게 되는 것입니다.


 ② Ordinal 변수 : 임포트한 함수에 대한 Ordinal 값을 명시할 때 사용하는 변수

    -  4Byte 크기를 가지는 IMAGE_THUNK_DATA32 구조체의 값이 MSB(최상위 비트)가 1이면 Ordinal 변수로 사용된 것입니다.

       다시 말해서 IMAGE_THUNK_DATA32 구조체의 값이 0x80000000 이상인 경우에 Ordinal 변수로 사용되어 임포트한 함수에 대한 Ordinal

       값을 명시한 것이며, MSB(최상위 비트)를 제외한 비트의 값이 Ordinal(서수)를 나타내게 됩니다.

                                  

 

- IAT를 구성하는 IMAGE_THUNK_DATA32 구조체의 변수 사용 용도에 관한 것은 IMAGE_IMPORT_DESCRIPTOR 구조체의 FirstThunk 멤버 

  변수와 관련 있습니다. 지금부터 IMAGE_IMPORT_DESCRIPTOR 구조체의 FirstThunk 멤버 변수에 대해서 알아보도록 하겠습니다.




2. FirstThunk 멤버 변수

- FirstThunk 멤버 변수는 IAT(Import Address Table)의 RVA 값을 가지는 멤버 변수입니다. IAT도 INT와 마찬가지로 IMAGE_THUNK_DATA32 구

  조체배열로 구성되어 있습니다. 그리고 INT는 IAT와 매우 유사합니다. 정확하게 말하자면, 바인딩(Binding)이라는 과정을 거치기 전에는 INT와 IAT는 

  완전하게 동일한 IMAGE_THUNK_DATA32 구조체 배열로 구성되어 있습니다. 하지만 바인딩 과정을 거치게 되면서 기존의 IAT의 IMAGE_THUNK

  _DATA32 구조체의 값들이 실제적인 함수의 주소로 바뀌게 됩니다.


 * 바인딩이란 : Windows 로더가 IAT(Import Address Table)의 정보를 통하여 해당 라이브러리에 연결되어 있는 다른 라이브러리를 같이 로드하며,

                        해당 라이브러리에서 참조하고 있는 다른 라이브러리의 함수 주소를 EAT(Export Address Table)에서 찾아 그 함수 주소를 IAT의 

                        IMAGE_THUNK_DATA32 구조체에 기록하는 것을 의미합니다.


- IAT 역시 IMAGE_THUNK_DATA32 구조체를 사용하므로 union으로 선언된 멤버 변수들을 상황에 따라 다른 변수들을 사용하게 되는데요. 

  각 멤버 변수들이 어떤 용도로 사용되는지 알아보도록 하겠습니다.

  ( IAT를 구성하는 IMAGE_THUNK_DATA32 구조체에서는 INT를 구성하는 구조체와는 달리 모든 멤버 변수들이 사용될 수 있습니다 )


 ① AddressOfData 변수 : IMAGE_IMPORT_BY_NAME 구조체의 주소(RVA)를 명시할 때 사용하는 변수

   -  INT를 구성하는 IMAGE_THUNK_DATA32 구조체에서 사용되는 용도와 동일합니다.


 ② Ordinal 변수 : 임포트한 함수에 대한 Ordinal 값을 명시할 때 사용하는 변수

    -  Ordinal 변수 사용 역시 INT를 구성하는 IMAGE_THUNK_DATA32 구조체에서 사용되는 용도와 동일합니다.


 ③ ForwarderString 변수 혹은 Function 변수 : 바인딩 과정을 거친 후, 실제 함수의 주소를 명시할 때 사용하는 변수

    -  IAT는 바인딩 과정을 거치기 전에는 INT와 동일한 IMAGE_THUNK_DATA32 구조체 배열을 가진다고 했었습니다. 하지만, 바인딩 과정을 

       거치게 되면 IAT의 IMAGE_THUNK_DATA32 구조체에서 사용하던 멤버 변수가 ForwarderString 혹은 Function 멤버 변수로 변경되어 

       사용되게 됩니다.




3. Name 멤버 변수

- IMAGE_IMPORT_DESCRIPTOR 구조체의 Name 멤버 변수는 해당 Library (DLL) 이름 문자열이 존재하는 주소(RVA)를 명시합니다.

  즉, 임포트하고자하는 Library 이름이 있는 곳을 가리키는 포인터 역할을 합니다.




4. TimeDateStamp 멤버 변수와 ForwarderChain

- 이 2개의 멤버 변수들은 PE 로더에 의해 각 IMAGE_IMPORT_DESCRIPTOR 구조체에 해당하는 라이브러리(DLL)가 바인딩 과정을 거치기 전에는

  항상 0의 값을 가지며, 바인딩이 완료된 후에는 -1의 값을 가지게 됩니다.



# 여기까지가 IAT의 IMAGE_IMPORT_DESCRIPTOR 구조체에 대한 내용이였습니다. 아직 IMAGE_IMPORT_BY_NAME 구조체 멤버 변수에 대해

   서는 언급하지 않았는데요. 마지막으로 IMAGE_IMPORT_BY_NAME 구조체에 대해서 설명하도록 하겠습니다. 



[ IMAGE_IMPORT_BY_NAME 구조체 (출처:winnt.h) ]


- IMAGE_IMPORT_BY_NAME 구조체는 IMAGE_THUNK_DATA32 구조체에서 AddressOfData 라는 멤버 변수를 사용할 때 사용되는 구조체

  입니다. 앞서 다 이야기 한 내용인데요. IMAGE_IMPORT_BY_NAME 구조체에 선언되어 있는 멤버 변수들은 어떤 정보를 의미하는 것인지 

  알아보도록 합시다.



1. Hint 멤버 변수

- PE 로더가 임포트하려는 함수를 빠르게 찾을 수 있도록 도와주기 위해 사용하는 멤버 변수로서, 임포트하려는 라이브러리의 EAT(Export 

  Address Table)의 인덱스 값을 가지고 있는 멤버 변수입니다. 이 멤버 변수의 값은 필수적이지 않기 때문에 0으로 채우져 있는 경우가 많습니다.



2. Name 멤버 변수

- 해당 라이브러리에서 임포트하고자 하는 함수의 이름을 명시하는 멤버 변수입니다.



# 이렇게 해서 IAT에서 사용되는 IMAGE_IMPORT_DESCRIPTOR 구조체와 멤버 변수에 대한 설명이 끝이 났습니다.

  이제 전체적인 IAT 모식도와 PE 로더가 임포트 함수 주소를 IAT에 입력하는 기본적인 순서에 대해서 알아보겠습니다.



[ MSpaint.exe 파일의 "ADVAPI32.dll"에 대한 IMAGE_IMPORT_DESCRIPTOR 구조입니다 ]


- 위 그림이 바로 전체적인 IAT 모식도입니다. NT Header에 포함하는 Optional Header의 멤버 변수인 IMAGE_DATA_DIRECT

  ORY 구조체 배열의 두 번째 구조체(Import)의 VirtualAddress 멤버 변수(IMAGE_IMPORT_DESCRIPTOR 구조체 시작주소)

  를 통하여 IMAGE_IMPORT_DESCRIPTOR 구조체 배열에서 각 라이브러리에 해당하는 함수에 대한 정보를 IMAGE_THUNK

  _DATA32 구조체와 IMAGE_IMPORT_BY_NAME 구조체를 통하여 알 수 있게 됩니다.


- 참고로 위 그림에서 IAT는 INT와 동일한 값들을 가지고 있는데, 바인딩 과정을 거치게 되면 실제 Function 멤버 변수로 실제 

  함수의 주소 값을 가지게 됩니다.




[ 출처 : 리버싱 핵심원리 ]




# MSPaint.exe (그림판) 프로그램을 대상으로하여 앞에서 이야기한 내용들을 실제로 살펴보도록 하겠습니다.


1. IAT(Import Address Table)를 찾기 위해서는 PE 파일에서 NT Header - Optional Header의 IMAGE_DATA_DIRECTORY [1]의 멤버 변수인

   VirtualAddress 값을 참고하여 IMAGE_IMPORT_DESCRIPTOR 구조체를 찾습니다. 헥사 에디터를 이용하여 해보도록 하겠습니다.




- IMAGE_DATA_DIRECTORY [1] 구조체의 VirtualAddress 멤버 변수 값("0008688C")을 구했습니다. 현재 이 주소 값은 IMAGE_IMPORT_

  DESCRIPTOR 구조체의 RVA 값입니다.


- VirtualAddress : 0008688C    /    Size : 00000168    


- 지금 우리가 알고 있는 VirtualAddress 값은 IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 RVA 값입니다. 따라서, RVA 값을 RAW 

  값으로 변경하고 해당 RAW로 찾아 가보도록 하겠습니다.


- 이전 포스팅 주제였던 "RVA to RAW" 내용이 여기에서 사용되네요. RAW 구하는 공식 기억하고 계시죠? 다시 한번 "RVA to RAW" 공식을 

  써보고 계산하도록 하겠습니다.



 RAW = RVA - VirtualAddress + PointerToRawData 


- 위 식에 해당 값들을 대입하게 되면,    RAW = 8688C - 1000 + 400 = "85C8C"     RAW 값 = "85C8C"

- 위의 계산식이 이해가 안가시면 이전의 "RVA to RAW" 포스팅을 다시 한번 보시길 바랍니다.


- RAW 값을 구했습니다. 우리가 구한 RAW 값인 "85C8C"는 IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소라고 했습니다. 헥사 

  에디터로 이동해보도록 하겠습니다.




- Offset "85C8C" 부터가 첫 번째 IMAGE_IMPORT_DESCRIPTOR 구조체의 멤버 변수들입니다. ( 구조체 하나당 20 Byte )

- IMAGE_IMPORT_DESCRIPTOR 구조체 배열에서 끝은 NULL 구조체로 끝이 난다고 했었습니다. 위 그림에서 가장 마지막 20Byte가 NULL로 

  구성된 것을 볼 수 있습니다.


  [ RVA 값 ]

- OriginalFirstThunk(INT) : 00086AD4

- TimeDateStamp : FFFFFFFF

- ForwarderChain : FFFFFFFF

- Name : 00086AC4

- FirstThunk(IAT) : 00001000


- 첫 번째 구조체에서 구한 멤버 변수들에 대한 값들은 모두 RVA 값입니다. 따라서, RAW 값으로 모두 변경하고 차례대로 해당 Offset을

  찾아보도록 하겠습니다.


  [ RAW 값 ]

- OriginalFirstThunk(INT) : 86AD4 - 1000 + 400 = 85ED4

- Name : 86AC4 - 1000 + 400 = 85EC4

- FirstThunk(IAT) : 1000 - 1000 + 400 = 400


# 참고로 OriginalFirstThunk, Name, FirstThunk 멤버 변수들의 RVA는 .text 영역에 해당하기 때문에, .text 영역의 VirtualAddres 값과

  그리고 PointerToRawData 값을 확인해보면 VirtualAddress = 1000 , PointerToRawData = 400 임을 확인할 수 있습니다.



- 각 멤버 변수들의 RAW 값으로 찾아가보도록 하겠습니다. 우선 Name 멤버 변수의 RAW 위치로 찾아가 첫 번째 구조체가 어떤 라이브러리

  인지 확인해 보도록 하죠.




- Name 멤버 변수의 RAW 위치인 "85EC4"로 찾아와 확인해보니 "ADVAPI32.dll" 이라는 것을 확인할 수 있습니다.



- 두 번째로 OriginalFirstThunk(INT) 멤버 변수의 RAW 위치로 이동해 보도록 하겠습니다. 




- OriginalFirstThunk(INT)가 가리키는 곳은 IMAGE_THUNK_DATA32 구조체 배열로 이루어진 INT 입니다. 각 구조체는 4Byte씩 구성

  되어 있으며, 마지막은 NULL 구조체로 구성되어 있습니다. 그리고 각 구조체 배열의 값은 일반적으로 IMAGE_IMPORT_BY_NAME 

  구조체를 가리키는 포인터 값으로 채워져 있습니다. 이 값 역시 RVA 값으로 되어 있습니다. 따라서, RVA 값을 RAW 값으로 변경한

  후 RAW 위치를 참조해야 합니다.


- 첫 번째 IMAGE_THUNK_DATA32 구조체 배열의 값인 "00087B04" RVA 값을 RAW 값으로 변경후 위치를 찾아가 보겠습니다.

- RAW = 87B04 - 1000 + 400 = "86F04"




- 보시는 것 처럼, IMAGE_IMPORT_BY_NAME 구조체 배열의 첫 번째 구조체의 멤버 값들입니다. 처음 2Byte는 Hint 멤버 변수의 값으로

  라이브러리에서 함수의 고유번호인 Ordinal을 의미합니다. 이어지는 멤버 변수는 함수 이름을 명시하는 Name 멤버 변수입니다. 이 멤버

  변수는 함수 명의 길이에 따라 크기가 가변적이며 마지막엔 NULL('\0')로 끝이 납니다.


- 마지막으로 확인해볼 FirstThunk(IAT) 멤버 변수의 RAW 값 위치를 확인해 보도록 하겠습니다.

  FirstThunk(IAT) : 1000 - 1000 + 400 = 400




- IAT 역시 IMAGE_THUNK_DATA32 구조체 배열로 구성되어 있고, 마지막엔 NULL 구조체로 구성됩니다. 이 구조체에서의 배열 값들은

  하드코딩되어 있는 값들로 MS사에서 서비스팩을 제작하면서 관련 시스템 파일을 재빌드할 때 정확한 주소를 하드 코딩한 것입니다.

  하지만, 이렇게 하드 코딩한 주소 값들은 운영체제 버전에 따라 하드 코딩된 주소 값이 정확하지 않는 경우도 있는데요.

  그런 경우에는 해당 프로세스가 실행될 때 PE 로더가 이 값들을 해당 API의 시작 주소 값으로 대체 시키게 됩니다. 따라서, 정확한

  주소 값으로 대체되는 것이죠. ( 바인딩 과정을 거쳐 실제 함수의 주소 값들로 채워지는 것입니다 )

  그리고, 일반적으로는 INT와 같은 값을 가지는 경우가 많습니다. 이런 경우 역시 바인딩 과정을 거치게 되면 실제 함수의 주소 값들로

  채워지게 됩니다.




# 지금까지 IAT(Import Address Table)에 대하여 설명했습니다. 공부하고 있는 학생이라 틀린 부분도 많이 있을 것 같은데요. 

  혹시 잘못된 부분들이 있으면, 댓글 달아주시면 수정하도록 하겠습니다. IAT 부분도 공부하면서 느낀거지만 정말 중요한

  내용인 것 같습니다. 여러번 보면서 익숙해질 수 있도록 해야겠습니다. 





# 참고 도서 및 참고 자료


- 리버싱 핵심원리

- Windows 구조와 원리

- PEFormat.pdf [PE 파일 전체 구조도] : PE 파일 전체 구조도가 정말 잘 정리되어 있습니다. 이 파일은 첨부하겠습니다.]

   

pe 전체 구조도.pdf


- 리버스 엔지니어링 바이블

http://stonycode.egloos.com/3191147

http://printf.egloos.com/2046777

http://bsodtutorials.blogspot.kr/2014/03/import-address-tables-and-export.html

http://zesrever.tistory.com/archive/20070908