PE IAT 실습

notepad.exe를 이용하여 PE 구조를 파악한다.

notepad.exe
0.06MB

먼저 IMAGE_OPTIONAL_HEADER의 DataDirectory 구조체의 배열 정보를 확인합니다.

RVA = 7604 인걸 알 수 있쥬?

실제 해당주소에 들어가 보면 앞쪽이 IMPORT Table의 RVA 뒤쪽이 Size입니다.

 

 

이후에 RVA를 알기 때문에 offset(RAW)을 구합니다.

7604 - 1000 + 0400 = 6A04 (text 섹션임)

파일에서 6A04을 보면 다음과 같습니다.

IID의 구조체 배열

블록으로 잡힌 부분이 전부 IID의 구조체 배열입니다.

RAW 값을 구해보면 다음과 같아집니다.

Description RAW
INT 00006D90
Time Date Stamp -
Forwarder Chain -
Name 00006EAC
IAT 000006C4

그럼 이제 하나씩 따라가 보자!

 

#1. Name 00006EAC

comdlg32.dll 문자열이 보인다!

 

#2. INT 00006D90

INT는 임포트 하는 함수의 정보가 담긴 구조체 포인터 배열입니다. 이 정보로 프로세스 메모리에 로딩된 라이브러리에서 해당 함수의 시작 주소를 알아낼 수 있습니다.

주소 값 하나하나가 각각의 IMAGE_IMPORT_BY_NAME 구조체를 가리키고 있습니다.

제일 첫 번째 배열 값인 7A7A를 따라가 봅시다.

 

#3. IMAGE_IMPORT_BY_NAME

RVA:7A7A를 RAW로 변환합시다.

7A7A+1000-0400 = 6E7A

 

제일 앞의 '..'(000F)는 라이브러리에서 함수의 고유번호라고 합니다. 이 Ordinal 뒤로 함수 이름 문자열이 보이죠?

여기까지 봤을 때 INT는 IMAGE_IMPORT_BY_NAME 구조체의 포인터 배열임을 알 수 있습니다.6

함수의 이름은 'PageSetupDlg\(?)' 이네요.

 

#4. IAT 000006C4

다음 그림에서의 블록영역이 'comdlg32.dll' 라이브러리에 해당하는 IAT 배열 영역입니다.

IAT의 첫 번째 원소값인 '76324906'은 의미 없는 값이고 메모리에 로딩될 시 정확한 주소값으로 대체됩니다.

 

그래서 ImageBase 01000000 + 000006C4 = 010006C4를 디버거로 열어보면

pagesetup 함수 시작이 나타나게 됩니다. 그값은 94269677 인데 실행과정에서 이값이 왜바뀌는지는 잘모르겠습니다.

=> OS에 따라서 바뀐다고 함.

 

아무튼 디버깅 실행후에 값을 010012C4에 값을 살펴보면 77962694 이고,

77962694로 바꼈음

77962694로 이동을 했을 시

짜잔!

이렇게 PageSetupdlg 함수의 시작부분을 확인할 수 있습니다.

 

헥사값이 바뀌는건 OS때문에 바뀌는거라고 생각됩니다...

어찌됐건 함수의 시작부분을 찾았다 !

 

PE EAT 실습

 

IAT를 실습했으니 이번엔 EAT를 실습해보자!

Windows OS에서 라이브러리는 다른 프로그램에서 불러 쓸 수 있도록 관련 함수를 모아놓은 파일을 의미.(SYS/DLL)

그 중 kernel32.dll 파일이 가장 핵심적인 라이브러리 파일이라고 할 수 있습니다.

 

EAT는 저번에도 봤었다시피 라이브러리에서 제공하는 함수를 다른 프로그램에서 가져다가 쓸 수 있도록 해주는 핵심 메커니즘 입니다. => EAT를 통해서만 해당 라이브러리에서 익스포트하는 함수의 시작 주소를 정확히 구별할 수 있음

 

앞의 IAT와 마찬가지로 IMAGE_OPTIONAL_HEADER의 DataDirectory에서 Export 정보를 저장해놓고 있음. 

 

kernel32.dll
1.17MB

해당 주소로 이동해보자. 00000168

IAT와 같이 앞쪽 4바이트가 RVA 뒤쪽 4바이트가 Size의 멤버임을 볼 수 있음.

 

RVA값이 262C 이므로 RAW를 계산해보면,

262C - 1000 + 0400 = '1A2C' 입니다.

 

다음으로 IMAGE_EXPORT_DIRECTORY의 구조체는 다음과 같습니다.

typedef struct _IMAGE_EXPORT_DIRECTORY {

DWORD Characteristics

DWORD TimeDateStamp

WORD MajorVersion

WORD MinorVersion

DWORD Name

DWORD Base

DWORD NumberOfFunctions

DWORD NumberOfNames

DWORD AddressOfFunctions // 함수 주소 배열(EAT)

DWORD AddressOfNames //함수명 배열

DWORD AddressOfNameOrdinals // 함수 서수 배열

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY

그럼 중요한 멤버들을 정리해보면,

항목 의미
NumberOfFunctions 실제 Export 함수 개수
NumberOfNames Export 함수 중에서 이름을 가지는 함수 개수 (<=NOF)
AddressOfFunctions Export 함수 주소 배열 (배열 원소 개수 = NOF)
AddressOfNames 함수 이름 주소 배열 (배열 원소 개수 = NON)
AddressOfNameOrdinals Ordinal 배열 (배열 원소 개수 = NON)

그렇쥬 ??

 

라이브러리에서 함수 주소를 얻는 API는 GetProcAddress() 입니다. 이 api가 EAT를 참조해서 원하는 API 주소를 획득함.

그럼 EAT를 정복하러 가즈아!

 

GetProcAddress() 동작 원리

1. AddressOfNames 멤버를 이용해 '함수 이름 배열'로 갑니다.

2. '함수 이름 배열'은 문자열 주소가 저장되어 있고, strcmp를 통하여 원하는 함수 이름을 찾습니다. (name_index)

3. AddressOfNameOrdinal 멤버를 통해 'ordinal 배열'로 갑니다.

4. 'ordinal 배열'에서 name_index로 해당 ordinal 값을 써치합니다.

5. AddressOfFunctions 멤버를 이용해 '함수 주소 배열(EAT)'로 갑니다.

6. '함수 주소 배열(EAT)'에서 아까 구한 ordinal을 배열 인덱스로 하여 원하는 함수의 시작주소를 얻습니다.

 

이 순서 그대로 실습 해보겠습니다.

 

#1. 함수 이름 배열

AddOfNames 멤버의 RVA = 3538

RAW = 3538 -1000+0400 = 2938 이를 Hex Editor로 살펴보면 다음과 같습니다.

 

 

#2. 원하는 함수 이름 써치

 

이 부분들이 이제 4바이트의 RVA로 이루어진 배열입니다. 배열 원소의 개수는 NumberOfName(3B9) 겠죠??

저 모든 RVA 값들을 하나하나 따라가면 함수 이름 문자열이 나오게 됩니다.

 

3번째 원소값을 볼까요? (4BB3 -> RAW : 3FB3)

보이시죠 AddAtom ㅎㅎ

* OS에 따라서 나뭇잎책과 값이 다를순 있으나 실습은 똑같음

 

'AddAtom'은 AddOfNames의 RVA들 중 3번째에 속해있고 배열 인덱스로는 '2'가 됩니다.

 

#3. Oridnal 배열

 

이제 'AddAtom' 함수의 Oridnal 값을 알아냅니다. AddressOfOrdinals 멤버값은 441C -> RAW : 381C 입니다.

다음과 같이 2바이트의 ordinal로 이루어진 배열이 나타나게 됩니다. 

 

#4. ordinal

 

아까 구했던 인덱스 '2'를 위의 Ordinal 배열에 적용시키면 Oridianl(2)를 구할 수 있습니다.

AddressOfNameOrdinal[index] = ordinal ( index = 2, ordinal = 2)

 

#5. 함수 주소 배열 - EAT

이제 마지막으로 AddAtom의 실제 함수 주소로 찾아갈 수 있습니다. AddressOfFunctions의 값은 2654 -> RAW : 1A54 입니다.

 

해당 주소를 보면 다음과 같이 4바이트 RVA 배열이 나타나게 됩니다. 이게 바로 함수의 주소들 입니다.

 

#6. AddAtom 함수 주소

 

함수 주소를 얻기위해 Ordinal을 index로 적용하면, 다음과 같이 RVA = 326D9를 얻을 수 있습니다.

현재 저의 kernel32.dll의 imagebase = 7C800000 이기 때문에 실제주소 VA = '7C8326D9' 입니다

이 값을 디버거에서 확인해보면 'AddAtom'이 잇겠지 ??

없다고 한다...

 

 

없어서 imagebase부터 살펴보니 pe viewer로 봤을때와 디버거에 올라갔을때 imagebase가 틀려짐을 확인했다.

 

틀려짐... kernel32.dll의 imagebase = 76FF0000 으로 바뀌었다.

여기에 RVA를 더해도 찾을수가 없었다.... 왜죠 ?

왜 여기가있죠????

모르겠다능  775e26d9

'Reversing' 카테고리의 다른 글

리버싱 8  (0) 2019.04.09
리버싱 7  (0) 2019.04.05
PE file(3)  (0) 2019.04.03
PE File(2)  (0) 2019.04.03
PE File(1)  (0) 2019.04.02

+ Recent posts