일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- oh-my-zsh
- NT Header
- NT File Header
- NT Optional Header
- RVA
- IMAGE_IMPORT_DESCRIPTOR
- sctf 2021
- DOS Header
- zsh theme customization
- GetProcAddress()
- stack based buffer overflow
- Windows Terminal
- BOF
- ubuntu
- 리버싱 핵심원리
- SQL Injection
- DOS Stub
- powerlevel10k
- PE Header
- PE file format
- attack vector
- Windows
- Tutorial
- WSL
- Windows 11
- web
- docker
- IMAGE_EXPORT_DIRECTORY
- Buffer Overflow
- samsung ctf
Archives
- Today
- Total
나만의 메모노트
[리버싱 핵심원리] PE File Format (4) 본문
IAT (Important Address Table)
- 개념 : 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블
- Windows OS의 핵심 개념인 Process, Memory, DLL 구조 등에 대한 내용이 함축되어 있음
- DLL 로딩 방식 중 Implicit Linking에 대한 메커니즘을 제공하는 역할
디버거를 통해 IAT 확인 해보기
- 대상 프로그램 : notepad.exe
Kernel32.dll의 CreateFileW() 함수 호출 - CALL [1001104] → notepad.exe에서 kernel32.dll의 CreateFileW를 호출하는 명령어
- CALL 명령어를 통해 호출하는 주소는 IAT 메모리 영역 (.text 섹션의 메모리 영역)
- 1001104 주소에 저장된 7C810CD9라는 값을 통해 CreateFileW를 호출 (직접 호출 X)
- 7C810CD9라는 값이 해당 프로세스 메모리에 로딩된 kernel32.dll의 CreateFileW 함수의 주소
- Windows API를 호출하는 일반적인 방식 중 하나
- CALL 명령어를 사용하지 않고 IAT 영역을 사용하는 2가지 이유
- 환경에 따라 달라지는 kernel32.dll의 버전과 CreateFileW 함수의 주소
- 컴파일러의 역할 : 해당 함수의 실제 주소가 저장될 공간을 마련한 후 CALL 명령어를 작성 ☞ 모든 환경에서 해당 함수 호출을 보장하기 위함
- 파일이 실행되는 순간 PE loader가 그 위치에 해당 함수의 주소를 입력
- DLL Relocation
- DLL이 ImageBase 값에 로딩되고, 또 다른 DLL도 같은 곳에 로딩을 시도하면 PE loader는 다른 비어 있는 메모리 공간을 탐색한 후 로딩한다. 이러한 이유는 실제 주소를 하드코딩 할 수 없으며, PE Header에서 주소를 나타낼 때 VA가 아닌 RVA를 써야하기 때문이다.
- ImageBase에 의한 DLL과 EXE 차이
- DLL : PE Header에 명시된 ImageBase에 로딩된다고 보장 할 수 없음
- EXE : 자신만의 가상 메모리 공간을 가지므로 자신의 ImageBase에 정확히 로딩됨
- 환경에 따라 달라지는 kernel32.dll의 버전과 CreateFileW 함수의 주소
DLL (Dynamic Linked Library)
- 개념 : 여러 프로그램에서 동시에 사용할 수 있는 코드와 데이터를 포함한 라이브러리
- 16bit DOS 환경
- DLL 개념이 존재하지 않음 👉 Library만 존재
- Multi-Tasking을 지원하는 Windows OS에 비해 비효율적
- 32bit Windows 환경
- 매우 많은 라이브러리 함수 사용 (process, memory, window, message 등)
- 문제점
- 여러 프로그램이 동시에 실행될 경우 모든 프로그램마다 매우 많은 라이브러리가 동일하게 포함되어 실행된다면 심각한 메모리 낭비를 불러옴. (디스크 공간마저 낭비)
- 해결방안
- DLL 개념 고안
- 프로그램에 라이브러리 포함 X → 별도의 파일(DLL)로 구성 → 필요 시 사용
- 한 번 로딩된 DLL의 코드, 리소스는 Memory Mapping 기술로 여러 Process에서 공유
- 라이브러리 업데이트 시 해당 DLL 파일만 교체 👉 쉽고 편리함
- DLL 개념 고안
- 실제 DLL 로딩 방식
- Implicit Linking (암시적 링크) : 프로그램 시작 시 함께 로딩, 프로그램 종료 시 메모리에서 해제
- 대부분의 프로그램에서 사용
- .dll과 .lib 파일이 모두 필요
- 프로그램 실행 시 DLL 내에서 호출한 함수가 있는지 검사 👉 없으면 프로그램 죽음
- 동적 링크된 참조가 있으면 프로그램이 실행될 때 해당 dll 파일을 프로세스의 주소 공간에 매핑 👉 dll이 없으면 프로세스 죽음
- Dll에 대한 진입점 함수 (초기화나 종료)가 있으면 OS가 그 함수를 호출 👉 없으면 프로그램 죽음
- Dll은 프로세스가 실행될 떄 한 번 로드되고 계속 프로세스 주소 공간에 남아 있음
- Dllmain 함수에 오류가 있거나 해당 dll이 없으면 프로세스가 바로 죽음
- 대부분의 프로그램에서 사용
- Explicit Linking (명시적 링크) : 프로그램에서 사용되는 순간 로딩, 사용이 끝나면 메모리에서 해제
- 실행 시점까지 무슨 dll을 로딩할 지 모를 경우 필요
- 설정 파일에 따라 로딩될 dll이 변경된 경우
- Dllmain 함수에 오류가 있거나 해당 dll이 없으면 경로를 재설정
- 실행 속도를 높이거나 필요 없는 dll을 실행 도중 해제하기 위해 사용
- 프로그램과 Import Library 사이의 연결이 필요 없음
- Explicit Linking에 사용되는 3가지 함수
- LoadLibrary
- 프로세스 내 주소 공간에 dll을 할당하는 함수
- 실패하면 NULL을 반환
- DLL 파일의 usage count를 1씩 증가
- GetPrcoAddress
- Export된 심볼의 시작 주소를 얻어옴
- FreeLibrary
- DLL 파일의 usage count를 1씩 감소시키고, 0이 되면 파일 이미지를 매핑 해제
- LoadLibrary
- 실행 시점까지 무슨 dll을 로딩할 지 모를 경우 필요
- Implicit Linking (암시적 링크) : 프로그램 시작 시 함께 로딩, 프로그램 종료 시 메모리에서 해제
IMAGE_IMPORT_DESCRIPTOR
- IMAGE_IMPORT_DESCRIPTOR 구조체
IMAGE_IMPORT_DESCRIPTOR 구조체
- 자신이 어떤 라이브러리를 Import하고 있는지 명시되어 있음
- Import : library에게 함수를 제공받는 일
- Export : library 입장에서 다른 PE 파일에게 함수를 제공하는 일
- 자신이 어떤 라이브러리를 Import하고 있는지 명시되어 있음
- 일반적인 프로그램 내에서 여러 라이브러리를 Import하므로 라이브러리의 개수만큼 구조체의 배열이 존재 (해당 구조체는 NULL 구조체로 끝남)
- 주요 멤버
멤버명 의미 OriginalFirstThunk INT(Import Name Table)의 주소(RVA) Name Library 이름 문자열의 주소(RVA) FirstThunk IAT(Import Address Table)의 주소(RVA) - 참고사항
- PE Header에서 Table은 배열을 의미함
- INT와 IAT는 long type (4바이트 자료형) 배열이고 NULL로 끝남 (크기가 따로 명시되어 있지 않음)
- INT에서 각 원소의 값은 IMAGE_IMPORT_BY_NAME 구조체 포인터 (IAT도 같은 값을 가지는 경우 있음)
- INT와 IAT의 크기는 같아야 함
- IMAGE_IMPORT_DESCRIPTOR 구조
notepad.exe의 kernel32.dll에 대한 IMAGE_IMPORT_DESCRIPTOR 구조
IAT 입력 순서
1. IID의 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 항목을 이용하여 해당 함수의 시작 주소 획득
➡ GetProcAddress("GetCurrentThreadId")
6. IID의 FirstThunk(IAT) 멤버를 읽어서 IAT 주소 획득
7. 해당 IAT 배열 값에 위에서 구한 함수 주소 입력
8. INT가 끝날 때까지(NULL을 만달 때까지) 위 4 ~ 7 과정 반복
9. INT와 IAT의 각 원소가 동시에 같은 주소를 가리키고 있지만, 그렇지 않은 경우도 많음
'Security > Reversing' 카테고리의 다른 글
[리버싱 핵심원리] DLL Injection (0) | 2021.08.01 |
---|---|
[리버싱 핵심원리] PE File Format (5) (0) | 2021.08.01 |
[리버싱 핵심원리] PE File Format (3) (0) | 2021.08.01 |
[리버싱 핵심원리] PE File Format (2) (0) | 2021.08.01 |
[리버싱 핵심원리] PE File Format (1) (0) | 2021.07.31 |