[KDT_AISEC] 8주차 - Windows PE 파일 구조 및 CrackMe 실습

Gloomy·2024년 1월 31일
0

KDT_AISEC

목록 보기
21/25

Windows PE File Format


윈도우 PE 파일 포맷이란?

PE파일은 Portable Executable파일로, 실행 파일을 의미한다. 실행 파일의 구조는 다음과 같다.

출처: https://0xrick.github.io/win-internals/pe2/


PEview라는 도구를 이용해 CrackMe 1번 파일을 열어보게 되면 파일에 담겨있는 정보를 구조에 맞춰 보기 쉽게 나눠준다.

DOS_HEADER


DOS_HEADER에는 파일의 시그니처와 EXE HeaderOffset정보가 담겨있다.

  • 파일 시그니처란 파일 내용을 식별하거나 확인하는 데 사용할 수 있는 파일 형식에 따른 고유의 포맷을 의미한다.

EXE HeaderOffset값인 0x100으로 가보면
PE라고 명시하고 있는 것을 볼 수 있다.

실행 파일은 무조건 MZ, PE를 가지며 이 중 하나라도 손상된다면 실행되지 않는다.

DOS_Stub

DOS_StubMS-DOS프로그램 코드이다. 파일마다 어떤 운영체제에서 실행해야 하는지, 도스 환경에서 실행할 수 있는지 불가능한지 등의 정보를 나타낸다.

현재 이 파일은 Win32환경에서 실행해야 한다고 명시되어있다.

NT HEADERS

NT HEADERS는 파일의 시그니처와 파일 헤더, 옵셔널 헤더가 있다.

파일 헤더에서는 섹션의 개수 등의 정보를 포함하고 있다.

옵셔널 헤더는 중요한 정보가 많은데, 첫 번째로 Image Base는 프로그램이 메모리상에 올라갔을 때 위치하는 주소이다. Address of Entry Point는 메모리 상에서 헤더 등을 제외하고 실제 프로그램의 내용이 위치한 곳의 주소인데, Image Base를 기준으로 한 상대주소이다. 즉 이 프로그램의 실제 주소는 0x4000000x1000을 더한 0x401000이 되는 것이다.

SECTION HEADER

다음은 섹션 헤더이다. 섹션 헤더에는 각 섹션에 대한 정보가 들어있으며, 해당 섹션이 메모리에 로드될 때 변하는 정보들이 들어있다.

여기서 중요한 것이 OffsetRVA, 그리고 VA이다.

Offset은 파일에서의 실제 위치를 뜻하고 RVA는 메모리 상의 상대주소, 마지막으로 VA는 메모리 상의 절대주소를 뜻한다.


이 헤더는 SECTION CODE의 헤더이다. Name은 섹션의 이름이고 Virtual Size는 해당 섹션이 메모리에서 차지하는 크기이다. Size of Raw Data는 파일에서 해당 섹션의 실제 크기를 의미하고 Pointer to Raw Data는 파일에서 해당 섹션의 시작 Offset을 의미한다.

이러한 정보를 바탕으로 디버거를 통해 각종 정보를 확인해보자.

CrackMe 실습


x64dbg 실행

수업 당시 실습은 ollydbg로 진행하였지만 다른 디버거도 사용해보고 싶어서 x64dbg로 진행해보기로 했다.

단축키는 ollydbg와 거의 유사한 것 같아서 따로 알아보지 않고 진행했다.

우선 CrackMe1번을 로드했다.

불러온 후 RUN을 한번 눌러주면 해당 파일의 엔트리 포인트로 이동시켜준다.

이제 각 위치를 살펴보자.

우선 이 파일의 이미지 베이스가 0x400000인지 확인해보자.

해당 주소를 확인해보니 정확하게 파일의 시작 지점이 들어가 있는 것을 볼 수 있다. 엔트리 포인트가 0x1000이었고, 0x401000을 확인해보면

파일의 진짜 데이터들이 들어가 있는 것을 확인할 수 있다.

여기서 한 가지 주의할 점은 엔트리 포인트가 코드의 메인은 아니라는 점이다. 메인이 아니더라도 엔트리 포인트일 수 있고, 이번 경우에는 우연히 둘이 일치한 것 뿐이다.

프로그램을 한 줄씩 실행시키면서 가보자.


윈도우 API인 MessageBoxA를 호출한 후 정상적으로 메시지 박스가 잘 나온다.

크랙미 1번은 하드 디스크를 CD-ROM으로 위장시키는 문제이다. GetDriveTypeA를 통해 현재 디스크의 타입을 반환한다.

https://learn.microsoft.com/ko-kr/windows/win32/api/fileapi/nf-fileapi-getdrivetypea

위 링크를 참조하면 해당 API의 정보가 나오는데, 해당 함수가 호출되면 반환값은

다음 중 하나이다.
해당 함수가 호출된 이후 반환값은 EAX레지스터에 저장되기 때문에 값을 살펴보면

3이 들어가 있고 이는

DRIVE_FIXED
3
드라이브에 고정 미디어가 있습니다. 예를 들어 하드 디스크 드라이브 또는 플래시 드라이브입니다.

을 의미한다.

구조를 보면

EAXESI에 특정 연산을 진행한 이후 비교했을 때 둘이 같다면 0x40103D로 점프하는 구문이다.

점프하는 위치에는

CD-ROM이 맞다는 메시지박스를 띄우는 구문이 존재하고 현재 프로그램은

EAXESI가 같지 않기 때문에 점프를 하지 않게 된다. 즉, 만약 CD-ROM에 해당하는 값으로 리턴이 된다면 점프를 하는 것으로 보인다. 이 문제를 풀기 위해서는 해당 점프 구문의 JEJNE로만 바꿔주면 된다.

해당 구문을 바꾼 후 실행해보자.

정상적으로 잘 우회한 모습이다. 이제 실제 파일을 크랙해보자.

아까 위에서 파일의 실제 Offset과 메모리 상의 주소는 다르다고 했었는데, 현재 메모리상의 절대주소, 즉 VA0x401026이다. 위에서 본 이 파일의 Image Base0x400000이었기 때문이 해당 주소의 RVA0x1026이다.

이제 RVAOffset으로 변환시켜주면 실제 파일에서 저 구문의 위치를 알 수 있게 되는데, 섹션 헤더를 보면

RVA0x1000이고 Virtual Size0x1000이므로 0x1000부터 0x1000만큼의 영역, 즉 0x1000 ~ 0x1fff를 의미한다.

0x1026은 이 영역에 해당하기 때문에 Pointer to Raw DataRVA의 차이를 구한 후 해당 값을 이용해 계산하면 된다.

차이가 0xA00이므로 0x1026에서 0xA00를 빼준다.
0x626이 해당 구문의 파일에서의 실제 Offset임을 알 수 있다. 그렇다면 이제 HxD를 이용해서 해당 파일을 크랙해보자.

실제로 잘 있는 것을 볼 수 있다. 이제 저 값을 0x74에서 0x75로 바꾼 후 저장해보자.


성공적으로 크랙을 완료하였다.

profile
𝙋𝙤𝙨𝙨𝙤 𝙁𝙖𝙧𝙚!

0개의 댓글