Rev
리버싱 핵심원리 2부
2024. 9. 23. 14:35

PE File Format

  • PE(Portable Executable) file : windows os에서 사용되는 실행 파일 형식
    • PE, PE32 - 32bit
    • PE+, PE32+ - 64bit
종류 주요 확장자 
실행 계열 exe, scr
라이브러리 계열 dll, ocx, cpl, drv
드라이버 계열 sys, vxd
오브젝트 파일 계열 obj

PE 파일 구조

PE파일이 메모리에 적재 (load, mapping)되는모습

  • FIle 에서는 offset으로, 메모리에서는 VA(virtual address)로 위치를 표현
  • 파일의 내용은 보통 .text, .data, .rsrc 섹션에 나눠 저장된다.
    • 섹션 헤더에 각 섹션에 대한 파일/메모리에서의 크기, 위치, 속성 등이 정의되어있다.
  • NULL padding?
    • 섹션의 시작위치는 각 파일/메모리의 최소 기본단위의 배수에 해당하는 위치여야하므로 빈공간을 null로 채움

RVA + ImageBase = VA

  • VA (virtual address)
    • 프로세스 가상 메모리의 절대 주소
  • RVA (Relative virtual address)
    • 어느 기준 위치 imagebase 에서부터의 상대 주소
    • PE 파일이 프로세스 가상 메모리의 특정 위치에 로딩되는 순간, 이미 그 위치에 다른 PE파일이 로딩되어있을 수 있음
      • relocation 재배치로 비어있는 다른 위치에 로딩되어야함
        • pe헤더 정보들이 rva로 되어있으면? 재배치가 발생해도 기준위치에 대한 상대주소가 변하지 않으므로 정상 작동 가능 (va는 X)

PE 헤더

DOS Header

  • 필수
    • e_magic : DOS signature (4D5A==”MZ”)
    • e_lfanew : NT header의 옵셋 표시 (파일에 따라 가변적인 값을 가짐) 아래 000000E0IMAGE_DOS_HEADER

DOS Stub (필수X 옵션)

NT Header

typedef struct _IMAGE_NT_HEADERS {
  DWORD                   Signature;   //PE
  IMAGE_FILE_HEADER       FileHeader;
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

 

  • File Header
    • 파일의 개략적인 속성을 나타내는 IMAGE_FILE_HEADER 구조체
    typedef struct _IMAGE_FILE_HEADER {
      WORD  Machine;
      WORD  NumberOfSections; //섹션 개수 >0
      DWORD TimeDateStamp; //빌드시간
      **DWORD PointerToSymbolTable;
      DWORD NumberOfSymbols;
      WORD  SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32 구조체 크기 or 64
      WORD  Characteristics; //파일속성**
    } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
    
    • Characteristics 값에 0002h없는 경우 (not executable)
      • .obj, resource DLL
    • IMAGE_FILE_HEADER

  • Optional Header
typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic; //**IMAGE_OPTIONAL_HEADER32 : 10B, 64: 20B**
  BYTE                 MajorLinkerVersion;
  BYTE                 MinorLinkerVersion;
  DWORD                SizeOfCode;
  DWORD                SizeOfInitializedData;
  DWORD                SizeOfUninitializedData;
  DWORD                AddressOfEntryPoint; //EP의 RVA갖고있음 프로그램이 최초로 시작되는 주소
  DWORD                BaseOfCode;
  DWORD                BaseOfData;
  DWORD                ImageBase; // PE파일이 로딩되는 시작주소
  DWORD                SectionAlignment; //메모리에서 섹션의 최소단위
  DWORD                FileAlignment; //파일에서 섹션의 최소단위
  WORD                 MajorOperatingSystemVersion;
  WORD                 MinorOperatingSystemVersion;
  WORD                 MajorImageVersion;
  WORD                 MinorImageVersion;
  WORD                 MajorSubsystemVersion;
  WORD                 MinorSubsystemVersion;
  DWORD                Win32VersionValue;
  DWORD                SizeOfImage; //로딩된 후 PE 이미지가 가상메모리에서 차지하는 크기
  DWORD                SizeOfHeaders; //PE헤더 전체 크기 FileAlignment 배수
  DWORD                CheckSum;
  WORD                 Subsystem; //.sys? .exe? .dll?
  WORD                 DllCharacteristics;
  DWORD                SizeOfStackReserve;
  DWORD                SizeOfStackCommit;
  DWORD                SizeOfHeapReserve;
  DWORD                SizeOfHeapCommit;
  DWORD                LoaderFlags;
  DWORD                NumberOfRvaAndSizes; //밑에 DataDirectory 배열 개수
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
  • PE 로더는 PE파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩후 EIP 레지스터 값을 ImageBase + AddressOfEntryPoint로 세팅
  • 파일 메모리 섹션 크기 : FileAlignment/SectionAlignment 배수.
  • Subsystem
IMAGE_SUBSYSTEM_NATIVE1  하위 시스템이 필요하지 않습니다(디바이스 드라이버 및 네이티브 시스템 프로세스).
IMAGE_SUBSYSTEM_WINDOWS_GUI2 Windows GUI(그래픽 사용자 인터페이스) 하위 시스템.
IMAGE_SUBSYSTEM_WINDOWS_CUI3 Windows CUI(문자 모드 사용자 인터페이스) 하위 시스템.
  • DataDirectory
    • 0 export
    • 1 import
    • 2 resource
    • 9 TLS directory

섹션 헤더

  • 각 섹션의 속성을 정의
  • 메모리 속성별 엑세스 권한
    종류  엑세스 권한
    code 실행 읽기
    data 비실행 읽기 쓰기
    resource 비실행 읽기
  • IMAGE_SECTION_HEADER
typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    **DWORD VirtualSize; //메모리에서 섹션이 차지하는 크기**
  } Misc;
  **DWORD VirtualAddress; //메모리에서 섹션의 시작주소 rva**
  **DWORD SizeOfRawData; //파일에서 섹션이 차지하는 크기**
  **DWORD PointerToRawData; //파일에서 섹션의 시작위치**
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  **DWORD Characteristics; //섹션의 속성(bit OR)**
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  • VirtualAddress PointerToRawData는 sectionalignment와 filealignment에 맞게 결정됨
  • Name은 null로 끝나지않고 아스키가 안와도됨

섹션 헤더 배열

RVA to RAW

  • PE파일이 메모리에 로딩되었을때 각 섹션에서 메모리주소rva와 파일 옵셋(raw)을 잘 매핑해야함
    1. RVA가 속한 섹션 찾기
    2. 간단한 비례식으로 raw계산
💡
RAW-PoinerToRawData = RVA - VirtualAddress
Raw = RVA - VirtualAddress + PointerToRawData

 

IAT (import Address Table)

  • process, memory, DLL 구조 등에 대한 내용 함축,
  • 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고있는지 기술한 테이블

DLL (Dynamic Linked Library)

  • 프로그램에 라이브러리를 포함시키지말고 별도의 파일(DLL)로 구성해 필요할때 쓰자
  • 일단 한번 로딩된 DLL코드, 리소스는 memery mapping 기술로 process에서 공유해 쓰자
  • 라이브러리가 업데이트되었을때 해당 DLL 파일만 교체하면 됨

DLL 로딩 방식 2가지

  • Explicit Linking
    • 사용되는 순간 로딩, 사용종료시 메모리 해제
  • Implicit Linking
    • 시작시 로딩, 프로그램 종료시 메모리 해제
    • IAT

실제주소를 하드코딩할 수 없는 이유, PE헤더 주소를 RVA써야하는 이유

  • 모든 환경에서 A 함수 호출을 보장하기 위해 컴파일러는 A의 실제주소가 저장될 위치를 준비하고 CALL DWORD PTR DS:[실제주소] 형식의 명령어를 적어두기만 한다.
  • 파일이 실행되는 순간 PE로더가 해당위치에 A주소를 입력해준다.
  • dll relocation 도 발생

IMAGE_IMPORT_DESCRIPTOR

  • import : 라이브러리에게 서비스 (함수)를 제공받는 일
  • export : 라이브러리 입장에서 다른 pe파일에게 서비스(함수) 제공하는 일
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;    // INT(import name table) 주소 rva     
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  
    DWORD   ForwarderChain;       
    DWORD   Name;  // 라이브러리 이름 문자열 주소 rva
    DWORD   FirstThunk;           //IAT(import address table) 주소 rva
} IMAGE_IMPORT_DESCRIPTOR;

EAT (Export Address Table)

  • 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 방식
  • EAT를 통해서만 해당 라이브러리에서 익스포트하는 함수의 시작주소를 정확히 구할 수 있음

IMAGE_EXPORT_DESCRIPTOR

 public struct IMAGE_EXPORT_DIRECTORY
    {
        public UInt32 Characteristics;
        public UInt32 TimeDateStamp;
        public UInt16 MajorVersion;
        public UInt16 MinorVersion;
        public UInt32 Name;
        public UInt32 Base;
        public UInt32 NumberOfFunctions; //실제 ecport 주소
        public UInt32 NumberOfNames; // export함수중 이름가지는 함수 개수
        public UInt32 AddressOfFunctions; // export 함수 주소 배열
        public UInt32 AddressOfNames; //함수 이름 주소 배열
        public UInt32 AddressOfNameOrdinals; // ordinal 배열
    }

실행 압축

데이터 압축

  • 비손실 압축
    • 100% 복원
    • 데이터 크기 줄여서 보관 및 이동에 용이하게 함
    • zip,rar
      • Run-Length, Lempel-Ziv, Huffman
  • 손실 압축
    • 100% 복원 X
    • jpg, mp3, mp4

실행 압축

  • 실행 파일 대상으로 파일 내부에 압축해제 코드를 포함하고있어 실행되는 순간 메모리에서 압축해제
  • 패커
    • 실팽파일 압축기
  • 프로텍터
    • 리버싱코드엔지니어링으로부터 보호하기위한 유틸리티
    • 리버싱을 막기위한 기법이 추가
                      •  

'Rev' 카테고리의 다른 글

리버싱 핵심원리 20장  (0) 2024.10.06
리버싱 핵심원리 16장  (0) 2024.09.28
KnockOn Bootcamp ROP  (0) 2024.09.18
KnockOn Bootcamp memory mitigation bypass  (0) 2024.09.18
KnockOn Bootcamp memory mitigation  (0) 2024.09.18
let textNodes = document.querySelectorAll("div.tt_article_useless_p_margin.contents_style > *:not(figure):not(pre)"); textNodes.forEach(function(a) { a.innerHTML = a.innerHTML.replace(/`(.*?)`/g, '$1'); });