본문 바로가기

2. Reversing (리버싱)

[2024.05.18] 리버씽씽카 6주차 활동

이번 리버싱 스터디 6주차에는 16-17강을 듣고 내용을 정리한 후 어려웠던 부분을 공유하며 질문을 통해 해결하는 시간을 가졌다.

참고한 유튜브 영상: 

https://www.youtube.com/playlist?list=PLY12b4RRLcSdsxgVvTW3mnNzMjVrd8JhO

 

리버싱 강의

리버싱에 대해 공부합시다.

www.youtube.com


<16강: IAT>

  • IAT: 어떤 프로그램에 어떤 라이브러리에 어떤 함수를 사용하고 있는지 기술한 테이블, 배열이다.
  • DLL (Dynamic Linked Library. 동적 연결 라이브러리)
    • MS-DOS 시절: 16비트 DLL 없었고 라이브러리만 있었음단점: 메모리 낭비 큼, 효율 안 좋음, 같은 작업 반복
    • ex. printf() → stdio.h 바이너리 코드(2진수) 코드를 긁어와서 필요한 프로그램에 삽입
    • window는 멀티 태스킹 지원해서 그게 안됨.
    • DLL은 동적으로 라이브러리 함수를 가져와서 연결 (파일 형태임)
    • DLL 초기 구상
      • 프로그램에 라이브러리 포함x. 별도의 파일(DLL)을 구상하여 필요할 때마다 호출
      • 일단 한 번 로딩된 DLL의 코드 리소스를 메모리 매핑 기술로 다 같이 나누어 쓰자
      • 라이브러리가 업데이트 했을 때 해당 DLL 파일만 교체하면 되어서 간편하다.
    • DLL 로딩 방식 2가지
      • explicit linking(명시적): 프로그램에서 사용되는 순간 연결(로딩)하고 사용 후 메모리에서 해제
      • implicit linking(암시적): 프로그램이 실행될 때 같이 연결(로딩)되어 프로그램 종료 시 메모리에서 해제
    • Kernel: OS admin 같은. 모든 것을 관리한다.
    • DLL 재배치: 어떤 파일의 IB가 10000인데 로딩 하려고 보니 다른 파일이 그 자리를 차지하고 있다면 다른 빈공간을 찾아서 로딩한다.

<17강: IAT(2)>

  • PE파일은 자신이 어떤 라이브러를 호출하고 있는지 IAT구조를 통해서 명시하고 있다.
typedef struct _IMAGE_IMPORT_DESCRIPTOR { 
union { 
DWORD   Characteristics;             
DWORD   OriginalFirstThunk;       
// INT(Import Name Table) address (RVA) 
}; 
DWORD   TimeDateStamp; 
DWORD   ForwarderChain; 
DWORD   Name;                         
DWORD   FirstThunk;                   
} IMAGE_IMPORT_DESCRIPTOR; 
// library name string address (RVA) 
// IAT(Import Address Table) address (R
typedef struct _IMAGE_IMPORT_BY_NAME { 
WORD    Hint;       //함수의 고유번호     
BYTE    Name[1];    //함수의 이름  
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; 

아래의 모든 주소는 RVA이다.

  • OriginalFirstThunk = INT(Import Name Table) 주소
  • Name = Libray 이름 문자열 주소
  • FirstThunk = IAT 주소를 가리킴

INT 와 IAT는 LongType 배열이고 NULL로 끝난다. 둘의 값은 같아야 한다.

 

IAT 입력 순서

  1. IMAGE_IMPORT_DESCRIPTOR의 Name 멤버를 읽고 나서 library 이름 문자열(“Kernel32.dll”)을 읽는다
  2. 해당 라이브러리를 로딩한다 → LoadLibrary(“Kernel32.dll”)
  3. IMAGE_IMPORT_DESCRIPTOR의 OriginalFirstThunk 멤버를 읽어서 INT 주소를 얻는다.
  4. 얻은 주소를 INT에서 읽어서IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻는다
  5. IMAGE_IMPORT_BY_NAME에서 Hint 또는 Name항목->함수의 시작주소를 얻는다 (GetProcAddress() 함수로)
  6. IAT 배열값에 4번의 함수주소를입력
  7. INT NULL 일 때까지 4-6 반복

IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress-> IMAGE_IMPORT_DESCRIPTOR

  1. 라이브러리 이름(Name) Name항목은 임포트 함수가 라이브러리에 파일 이름 문자열 포인터이다.
    ⇒ IAT 입력순서 1
  2. OriginalFirstThunk - INT(Import Name Table) INT = 임포트 하는 함수의 정보(Ordinal)가 담긴 구조체 포인터 배열이다. 이 정보는 필요로 하는 프로세스 메모리에 로딩된 라이브러리에서 해당 함수의 시작주소를 정확히 구할 수 있다. → IMAGE_IMPORT_BY_NAME(INT의 구조체)
  3. Ordinal 고유번호. 함수들이 변수들을 구별하기 위한 번호이다. IMAGE_IMPORT_BY_NAME은 DLL에서 함수를 호출할 때 필요한 정보(이름, 고유번호)를 담고 있다. 그 정보를 토대로 주소를 서치한다.
    ⇒ IAT 입력 순서 2-5
  4. FirstThunk - IAT(Import Address Table) FirstThunk는 IAT를 가리키는 포인터이다.

 

최종 정리를 해보자면:

  • OriginalFirstThunk는 INT를 가리킨다.
  • INT와 IMAGE_IMPORT_BY_NAME의 차이점:
    • INT - 함수 이름 & 고유번호를 담는다.
    • IMAGE_IMPORT_BY_NAME - 해당 함수에 대한 자세한 정보를 담는다.
  • NAME과 IMAGE_IMPORT_BY_NAME의 차이점:
    • NAME - 가져와야하는(필요한) DLL 파일의 이름을 가리킨다.
    • IMAGE_IMPORT_BY_NAME - DLL에서 내보내야하는 함수의 이름과 고유번호 값을 가지고 있는 구조체이다.
  • IAT: 실행파일(ex. DLL)이 다른 모듈(exe)에서 사용하는 함수를 가리키는 포인터 배열이다. 즉, 모든 함수 주소들의 집합.