일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- leetcode 5
- airflow docker
- 머신러닝
- leetcode125
- leetcode 49
- Hadoop
- Hortonworks Sandbox
- 블로그 이전했어요
- webcrawler
- Python
- 빅데이터를 지탱하는 기술
- leetcode 344
- 올바른 변수명 짓기
- 컴퓨터구조
- leetcode 819
- MapReduce 실습
- docker로 airflow 설치하기
- leetcode 238
- 문자열 조작
- 데이터레이크와 데이터웨어하우스
- ctf-d
- leetcode 121
- leetcode
- leetcode 937
- wargame.kr
- leetcode 15
- 스파크 완벽 가이드
- leetcode 561
- 배열
- leetcode 234
- Today
- Total
HyeM
[3]IAT, EAT 로딩 과정 본문
IAT
1. 설명 :
IAT는 DLL(동적 연결 라이브러리)를 구현할 때, DLL 로딩 방식 중 하나이다.
+ DLL 로딩 방식에는 2가지 방식이 있는데, 하나는 Explicit Linking이고 다른 하나는 Implicit Linking(IAT)이다.
프로그램 실행할 때 같이 로딩되어, 프로그램이 종료될 때 메모리에서 해제되는 방법이다.
( PE파일이 어떤 라이브러리를 import하고 있는지 table에 기술함. )
2. 관련 구조체
(1) IMAGE_IMPORT_DESCRIPTOR (IID) = IMPORT Directory table
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
};
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
: PE파일이 어떤 라이브러리를 import하고 있는지 이 구조체에 명시함.
라이브러리 개수 만큼 구조체의 배열 형식으로 존재한다. 배열 마지막은 NULL이다.
ex)[0] KERNEL32.dll(IID) , [1] VERSION.dll (IID) .... NULL
: 주요 멤버
OriginalFirstThunk : INT(Import Name Table)의 주소(RVA)
Name : Library이름 문자열 주소 (RVA)
FirstThunk : IAT(Import Address Table)의 주소(RVA)
: PE 로더가 import 함수 주소를 IAT에 입력하는 기본적인 순서
- IID의 Name 멤버를 읽어 라이브러리 이름 문자열 (kernel32.dll) 을 얻는다.
- 해당 라이브러리를 로딩한다.
- IID의 OriginalFirstThunk 멤버를 읽어서 INT주소를 얻는다.
- INT에서 배열의 값을 하나씩 읽어, 해당 IMAGE_IMPORT_BY_NAME 주소 (RVA)를 얻는다.
- IMAGE_IMPORT_BY_NAME의 Hint(ordinal)또는 Name항목을 이용하여 해당 함수의 시작 주소를 얻는다.
- IID의 FirstThunk(IAT)멤버를 읽어서 IAT주소를 얻는다.
- 해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.
- INT가 끝날때 까지(NULL)을 만날때 까지 위의 4~7 과정 반복
(2) IMAGE_IMPORT_BY_NAME
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
(3) IMAGE_THUNK_DATA32
typedef struct _IMAGE_THUNK_DATA32{
union{
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
}IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
INT와 IAT를 구성하고 있는 구조체.
OriginalFirstThunk와 FirstThunk는 IMAGE_THUNK_DATA32 구조체 배열을 갖고 있다.
3. 실습:
[ADVAPI32.dll 찾아가기]
자세히 Data Directory_ IMAGE_DATA_DIRECTORY
DataDirectory[0]=EXPORT Directory
DataDirectory[1]=IMPORT Directory
....
DataDirectory[9]=TLS Directory
NT 헤더의 Data Directory에서 Import Directory (즉, DataDirectory[1]) 구조체를 찾아감.
+ Data Directory는 IMAGE_DATA_DIRECTORY 구조체 배열로, 배열의 각 항목마다 정의된 값을 갖는다.
RVA가 0000 A048이다.
RVA to RAW 공식으로 file offset을 구하면 다음과 같다.
ㄴ RAW(FileOffset) = RVA - VirtualAddress + PointerToRawData
=> 0000 A048 - 0000 1000(A048은 .text영역이다.) +0000 0400 = 0000 9448 --> Import Directory 시작 offset
File Offset | Member | RVA | RAW |
9448 | OriginalFirstThunk(INT) | 0000 A234 | A234-1000+0400=9634 |
944C | TimeDateStamp | FFFF FFFF | - |
9450 | ForWarderChain | FFFF FFFF | - |
9454 | Name | 0000 A224 | A224-1000+0400=9624 |
9458 | FirstThunk(IAT) | 0000 1000 | 1000-1000+0400=0400 |
1. NAME
9624 Offset에 "ADVAPI32.dll" 문자열 있음.
2. OriginalFirstThunk-INT(Import Name Table)
INT는 함수의 정보가 담긴 구조체 포인터 배열이다. 이를 통해, 프로세스 메모리에 로딩된 라이브러리에서 해당 함수의 시작 주소를 알 수 있음.
9634 Offset에 가면 INT 있음.
주소 배열 형태로 되어 있고, 배열 끝은 NULL이다. 주소 값 하나하나가 각각의 IMAGE_IMPORT_BY_NAME 구조체를 가르킨다.
배열 첫번째 값인 0000A634(RVA)를 따라가 본다. 따라가면 임포트하는 API함수 이름 문자열이 있다.
3. IMAGE_IMPORT_BY_NAME
A634(RVA) -> 9A34(RAW)
파일 옵셋 9A34의 최초 1바이트(007E)는 Ordinal로, 라이브러리에서 함수의 고유 번호이다.
그 뒤로 'RegSetValueExW' 함수 이름의 문자열이 보인다.
==> INT는 IMAGE_IMPORT_BY_NAME의 구조체 포인터 배열이다. 배열의 첫번째 원소가 가리키는 함수의 Ordinal값은 007E이고, 함수의 이름은 RegSetValueExW 이다.
4. FirstThunk -IAT(Import Address Table)
0400offset
ADVAPI32.dll 라이브러리에 해당하는 IAT 배열 영역이다. 첫번째 원소 값은 이미 77C71C82 로 하드 코딩 되어 있음.
원래는 notepad.exe 파일이 메모리에 로딩될 때 이 값은 정확한 주소 값을 대체 되어야 한다.
하지만, 실습 환경 설정이 잘 안 맞았는지, 원하는 결과가 나오지 않았다.
EAT
1. 설명
라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 핵심 매커니즘
( 라이브러리가 가진 함수를 다른 프로그램에서 사용할 수 .있도록 함)
2. 관련 구조체
IMAGE_EXPORT_DIRECTORY
EAT는 IAT와 마찬가지로 PE 파일 내에 특정 구조체(IMAGE_EXPORT_DIRECTORY)에 정보를 저장함.
이 구조체는 PE파일에 하나만 존재한다.
ypedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp; // creation time date stamp
WORD MajorVersion;
WORD MinorVersion;
DWORD Name; // address of library file name
DWORD Base; // ordinal base
DWORD NumberOfFunctions; // number of functions
DWORD NumberOfNames; // number of names
DWORD AddressOfFunctions; // address of function start address array
DWORD AddressOfNames; // address of function name string array
DWORD AddressOfNameOrdinals; // address of ordinal array
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
: 중요 멤버
NumberOfFunctions : 실제 Export함수 개수
NumberOfNames : export 함수 중에서 이름을 가지는 함수 개수 ( <=NumberOfFunctions)
AddressOfFunctions : export함수 주소 배열 ( 배열의 원소 개수 = NumberOfFunctions)
AddressOfNames : 함수 이름 주소 배열 ( 배열의 원소 개수 = NumberOfNames )
AddressOfNamesOrdinals : Ordinal 배열 ( 배열의 원소 개수 = NumberOfNames )
: EAT를 참조해서 원하는 API주소 구하기
(라이브러리에서 함수 주소를 얻는 API는 GetProcAddress() 이다. )
- AddressOfNames 멤버를 이용해 '함수 이름 배열' 로 간다.
- '함수 이름 배열'은 문자열 주소가 저장되어 있다. 문자열비교(strcmp)를 통하여 원하는 함수 이름을 찾는다.(이때 배열의 index를 name_index로 하자.)
- AddressOfNameOrdinals 멤버를 이용해 'ordinal배열' 로 간다.
- 'ordinal배열'에서 name_index로 해당 ordinal값을 찾는다.
- AddressOfFunctions 멤버를 이용해 '함수 주소 배열(EAT)'로 간다.
- '함수 주소 배열(EAT)'에서 아까 구한 ordinal을 배열 인덱스로 하여 원하는 함수의 시작 주소를 얻는다.
+ ordinal은 export function 의 고유번호이다.
3. 실습
[kernel32.dll 파일 EAT에서 AddAtomW 함수 주소 찾기] _win10 64bit에서 진행
1. NT 헤더의 Data Directory에서 Export Directory (즉, DataDirectory[0]) 구조체를 찾아감.
( = IMAGE_EXPORT_DIRECTORY 구조체 배열의 시작 주소)
RVA는 0008 EC80이다.
RVA TO RAW공식으로 file offset을 구하면 다음과 같다.
ㄴ RAW(FileOffset) = RVA - VirtualAddress + PointerToRawData
==> 0008 EC80 - 0007 6000(8 EC80은 .rdata 영역이다) + 0007 4C00 = 0008 D880 --> Export Directory 시작 offset
File Offset | Member | Value | RAW |
8 D880 | Characteristics | 0000 0000 | - |
8 D884 | TimeDateStamp | 2D28261F | - |
8 D888 | MajorVersion | 0000 | - |
8 D88A | MinorVersion | 0000 | - |
8 D88C | Name | 0009 2C4A | 0009 184A |
8 D890 | Base | 0000 0001 | - |
8 D894 | NumberOfFunctions | 0000 065D | - |
8 D898 | NumberOfNames | 0000 065D | - |
8 D89C | AddressOfFunctions | 0008 ECA8 | 0008 D8A8 |
8 D8A0 | AddressOfNames | 0009 061C | 0008 F21C |
8 D8A4 | AddressOfNameOrdinals | 0009 1F90 | 0009 0B90 |
GetProcAddress() 동작 원리의 순서대로 진행함.
1. 함수 이름 배열
AddressOfNames raw값은 0008 F21C이다.
여기는 4Byte의 RVA로 이루어진 배열로, 배열의 원소 개수는 NumberOfNames이다.
이 모든 RVA값을 하나하나 따라 가면 함수 이름 문자열이 나타난다.
2. 원하는 함수 이름 찾기
내가 찾고자 하는 'AddAtomW' 함수 이름 문자열은 앞의 그림 에서 RVA배열의 여섯번재 원소값 (RVA: 0009 2CF0 ->RAW : 0009 18F0 )를 따라가면 된다.
'AddAtomW' 함수 이름은 AddressOfNames 배열의 6번째 원소 이고, 배열의 인덱스로는 5이다.
3. Ordinal 배열
'AddAtomW'의 Ordinal값을 알아내보려 한다.
AddressOfNamesOrdinals의 멤버의 값의 RAW는 0009 0B90 이다.
2byte로 이루어진 배열이 나타난다. (ordinal 배열의 각 원소의 크기는 2바이트 이다.
'AddAtomW' 는 AddressOfNames의 배열의 인덱스로 5였으니까, 위의 그림에서 다음 위치에 해당한다.
4. ordinal
2번에서 구한 index값 (5)를 3번의 Ordinal 배열에 적용하면 Ordianal(5)를 구할 수 있다.
AddressOfNameOrdinals[index] = ordinal (index=5, ordinal=5 )
5. 함수 주소 배열 - EAT
마지막으로 'AddAtomW'의 실제 함수 주소로 찾아가면 된다.
AddressOfFunctions 멤버의 값의 RAW는 0008 D8A8 이다.
4바이트 함수 주소 RVA배열이 나타난다. 여기 있는것들이 Export 함수 주소들이다.
6. AddAtomW 함수 주소
AddAtomW 함수 주소를 얻기 위해, 3단계에서 얻은 Ordinal 값을 5단계 배열 index로 적용하면 , RVA=0001 0890값을 얻을 수 있다.
AddressOfFunctions[ordinal] = RVA (ordinal=2, RVA= 1 0890)
'AddAtomW' 함수의 실제 주소(VA)를 구하기 위해서는 'kernel32.dll 의 ImageBase'값과 위에서 구한 1 0890을 더하면 된다.
먼저, ImageBase값을 알기 위해, 툴로 열어서 확인해보니, 다음과 같았다.
( ImageBase는 NT Header > Optional Header > Image Base)
32bit가 아닌 64bit로 실습해서 그런지, imageBase 값이 Qword로 되어있었다.
qword로 주소 계산을 하니까, 생각보다 더 큰 주소값이 나왔고 이는 실제 디버거로 열었을 때 주소가 될 수 없었다.
결론적으로 이 마지막 실습은 성공하진 못했지만, dll 파일에서 export 함수 주소를 찾아내는 방법을 알 수 있었다.
'Study > Reversing' 카테고리의 다른 글
DLL Injection(개념+실습) (0) | 2020.11.05 |
---|---|
[3]패킹&UPX패킹(디버거로 언패킹) (0) | 2020.10.26 |
[3]Dreamhack_Rev 4번 문제 (0) | 2020.10.12 |
[3]Dreamhack_Rev 5번 문제 (0) | 2020.10.12 |
[2]PE구조&wow64 fs redirection + a (0) | 2020.09.20 |