ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • PE 구조 (5) - 계산기의 PE 구조 / NT HEADER - IMAGE OPTIONAL HEADER 분석
    Knowledge/Reversing 2019. 9. 25. 02:11

     

     

    오늘은 계산기의 NT Header에서 IMAGE OPTIONAL HEADER에대해서 분석하려고 한다.

    꽤나 중요한 녀석임으로 차근차근히 분석을 시작해보자.

     

    우선 복습을 위하여 몇 가지 저번에 배운 내용을 확인해보자.

     

    이것이 calc.exe의 NT header 영역이다.

     

    그리고 NT header 구조체는 

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

    로 정의되어 있고 지난 포스터에서 FileHeader 부분을 알아봤었다.

     

    https://shineild-security.tistory.com/73

     

    PE 구조 (4) - 계산기의 PE 구조 / NT HEADER - IMAGE FILE HEADER 분석

    오늘 알아볼 내용은 NT Header이다. NT Header는 3가지 변수로 정의되어 있는 구조체이다. typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 Optio..

    shineild-security.tistory.com

     

     

    오늘 알아볼 구조체는 IMAGE_OPTIONAL_HEADER이다.

    일단 정의된 구조를 확인해보자.

     

     

    typedef struct _IMAGE_OPTIONAL_HEADER {
        //
        // Standard fields.
        //

        WORD    Magic;
        BYTE    MajorLinkerVersion;
        BYTE    MinorLinkerVersion;
        DWORD   SizeOfCode;
        DWORD   SizeOfInitializedData;
        DWORD   SizeOfUninitializedData;
        DWORD   AddressOfEntryPoint;
        DWORD   BaseOfCode;
        DWORD   BaseOfData;

        //
        // NT additional fields.
        //

        DWORD   ImageBase;
        DWORD   SectionAlignment;
        DWORD   FileAlignment;
        WORD    MajorOperatingSystemVersion;
        WORD    MinorOperatingSystemVersion;
        WORD    MajorImageVersion;
        WORD    MinorImageVersion;
        WORD    MajorSubsystemVersion;
        WORD    MinorSubsystemVersion;
        DWORD   Win32VersionValue;
        DWORD   SizeOfImage;
        DWORD   SizeOfHeaders;
        DWORD   CheckSum;
        WORD    Subsystem;
        WORD    DllCharacteristics;
        DWORD   SizeOfStackReserve;
        DWORD   SizeOfStackCommit;
        DWORD   SizeOfHeapReserve;
        DWORD   SizeOfHeapCommit;
        DWORD   LoaderFlags;
        DWORD   NumberOfRvaAndSizes;
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

     

    가장 마지막 변수를 보면 IMAGE_DATA_DIRECTORY 라는 구조체 배열이 나온다.

    IMAGE_NUMBEROF_DIRECTORY_ENTRIES는 16으로 정의되어 있다.

    그리고 IMAGE_DATA_DIRECTORY 구조체의 구조를 보면

     

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;
        DWORD   Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

     

    로 정의되어 있는 것을 확인 할수 있다.

     

     

    정의된 구조체의 크기를 계산해보면 1 * 2 + 2 * 9 + 4 * 19 + 8 * 16 = 224 (0xE0)으로 앞서서 IMAGE FILE HEADER에 정의되있던 크기인 E0과 같은 것을 확인 할 수 있다.

     

     

    여러 변수들이 선언되어있는데 우리가 알아봐야할 것은 일부분이다.

     

    Magic : 32 & 64 bit 표현
    AddressOfEntryPoint : 프로그램의 시작주소
    ImageBase + AddressOfEntryPoint = 상대적 시작 주소
    BaseOfCode : code 영역의 시작 주소
    BaseOfData : data 영역의 시작 주소
    SectionAlignment : 메모리 상의 최소 단위
    FileAlignment : 파일 상의 최소 단위
    SizeOfImage : PE 파일이 메모리에 load되어 있는 상태의 크기
    SizeOfHeader : PE 헤더의 전체 크기
    Subsystem : 1 - Driver, 2 - GUI, 3 - CUI
    NumberOfRavAndSizes : IMAGE_DATA_DIRECTORY DataDirectory의 배열 길이

    DataDirectory : 각 항목별로 정의된 값이 존재한다.

    // Directory Entries

    #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
    #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
    #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
    #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
    #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
    #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
    #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
    //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
    #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
    #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
    #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
    #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
    #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
    #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
    #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
    #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

    #define IMAGE_DIRECTORY_ENTRY_RESERVED 15 // Reserved Directory

     

    우리는 여기서 EXPORT(0), IMPORT(1), RESOURCE(2), TLS(9)만 집중해서 보면 된다.

     

     

    그럼 이제 하나하나 값을 확인해보자!

     

     

     

     

    MAGIC : 0B 01 (10B)

    10B : 32 bit

    20B : 64 bit

     

    AddressOfEntryPoint : 75 24 01 00 (12475)

    Entry Point의 RVA 값을 갖고 있다.

    즉 프로그램이 시작되면 최초로 실행되는 코드의 시작 주소이다.

     

    한번 확인해보자.

     

    올리디버거를 키면 12475부터 시작하는 것을 확인할 수 있다!

     

     

    BaseOfCode : 00 10 (10 00)

    code 영역의 시작 주소이다.

    밑에 언급한 imagebase의 주소의 값과 더한 1001000에 코드영역이 시작되는지 확인해 보자.

     

     

     

    BaseOfData : 00 40 01 (14000)

    아까 위에 사진을 보면 ASCII dump 칸을 보면 01014000에서부터 시작하는 것을 확인할 수가 있다!

     

     

    ImageBase : 00 00 00 01 (1000000)

    프로세스 가상메모리의 값으로 프로그램이 로드될때 eip는 imagebase값과 AddressOfEntry point 값을 더해서 로딩이 된다.

    앞서 baseofcode와 baseofdata를 보면 알겠지만 실제 메모리에 맵핑될 때 더해지는 값이다.

     

     

    SectionAlignment : 00 01 (100)

    메로리상의 최소 단위로 100(h) 단위로 패딩된다.

     

     

    FileAlignment : 00 02 (200)

    파일 상의 최소 단위가 200으로 패딩된다.

     

     

    SizeOfImage : 00 F0 01 (1F000)

    PE 파일이 메모리에 로딩되엇을 때 가상 메모리에서 PE Image가 차지하는 크기가 1F000이라는 뜻이다.

     

     

    SizeOfHeader : 00 04 (400)

    PE header의 전체 크기로 이전글들에서 똥꼬쇼하며 구했던 녀석이 여기 있다...

     

     

    Subsystem : 02

    1 : Driver file = 시스템 드라이버 (ex: ntfs.sys)

    2: GUI file = 창 기반 애플리케이션 (ex: notepad.exe)

    3: CUI file = 콘솔 기반 애플리케이션 (ex: cmd.exe)

     

     

    NumberOfRvaAndSizes :  10

    가끔 16이 아닐 경우도 있으니 확인을 꼭 해주자!

    지금 같은 경우는 정의된 경우와 같이 16값을 나타내고 있다.

     

     

     

     

     

    대망에 DataDirectory부분이다.

    4바이트 변수 2개를 갖고 있는 구조체 배열로 하나당 8bytes씩 보면 된다.

    첫번째는 가상주소 값이고 두번째는 사이즈를 표시한다.

     

    [0]에는 값이 없으므로 넘어가고 [1]인 Import값을 확인해 보자.

    80 2B 01이 주소로 적혀있다. 확인해보자.

    12B80은 RVA의 값으로 offset값을 계산해야하는데 이건 섹션별 주소를 알때 계산이 쉽다.

    고로 이 부분은 섹션 헤더를 정리한 후에 따로 작성하도록 하겠다.

     

    절대 졸려서 그만 쓰는거 아닙니다. 

     

     

    댓글

Designed by Tistory.