e_yejun
Jun_ : Pwn
e_yejun
전체 방문자
오늘
어제
  • 분류 전체보기 (240)
    • Profile (1)
    • Pwnable (54)
    • Reversing (14)
    • Network (7)
    • Forensic (10)
    • Embedded (4)
    • Android (2)
    • Web (18)
    • 알고리즘 (42)
    • 프로그래밍 (24)
    • 프로젝트 (6)
    • 1-day (7)
    • CTF (15)
    • 기타 (33)
    • 일기장 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • how2heap
  • x64
  • dvwa
  • BOF
  • dreamhack.io
  • 1-day
  • wargame
  • rev-basic
  • X86
  • Heap

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
e_yejun

Jun_ : Pwn

Reversing

[Dreamhack] rev-basic-4 write up

2023. 1. 22. 17:44

Index

문제
문제풀이
파이썬 풀이 코드
실행 결과
Synchronize IDA Assembly
파이썬 풀이 코드
실행 결과


rev-basic-4
Reversing Basic Challenge #4 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다. 해당 바이너리를 분석하여 correct를 출력하는 입력값을 알아내세요. 획득한 입력값은 DH{} 포맷에 넣어서 인증해주세요. 예시) 입력 값이 AppleBanana일 경우 flag는 DH{AppleBanana} Reference Reverse Engineering Fundamental Roadmap
https://dreamhack.io/wargame/challenges/18/

문제

실행 파일(exe)이 주어지고, 실행하면 사용자 입력을 받고 프로그램이 종료된다.

문제풀이

문제의 main 함수이다. 이전 rev-basic-3 문제와 같은 main 형태를 가지고 있다. 바로 sub_140001000 함수로 들어가보자.

rev-basic-3문제를 풀면서 알다싶이, for문 안에 if 구문을 역계산하면 문제가 해결된다.

if ( ((unsigned __int8)(16 * *(_BYTE *)(a1 + i)) | ((int)*(unsigned __int8 *)(a1 + i) >> 4)) != byte_140003000[i] )

디컴파일 된 복잡한 if 구문이다. 이를 편하게 해석하면 다음과 같다.

if (16 * a1[i] | a1[i] >> 4)) != byte_140003000[i] )

<< 또는 >> 은 비트를 옮기는 시프트 연산자 이다.

만약 a1[0]의 값이 24라면, bit로 나타내면 0001 1000 이다.

24 >> 4 는 오른쪽으로 4칸을 옮겨서 0000 0001이 된다.

24 << 4 는 왼쪽으로 4칸 옮겨서 1 1000 0000이 되지만, 1byte 라면 1000 0000이 된다.

💡
시프트 연산을 n만큼 한다는 것은 a1[i] * 2**n과 같기 때문에, 16 * a1[i]와 a1[i] << 4는 같다.

전구의 내용을 적용하면 조금 더 이쁘게 수식을 만들 수 있다.

if (a1[i] << 4 | a1[i] >> 4)) != byte_140003000[i] )

이를 역 연산 하면 문제를 해결할 수 있다.

byte_140003000에 어떤 값이 들어가있는지 F2로 브레이크 포인터를 걸고 F9로 실행시켜 메모리 주소로 찾아가서 확인했다.

byte_7ff6522f3000 주소로 찾아가면 저장된 hex값을 확인할 수 있다.

파이썬 풀이 코드

str = [0x24, 0x27, 0x13, 0xc6, 0xc6, 0x13, 0x16, 0xe6, 0x47, 0xf5, 0x26, 0x96, 0x47, 0xf5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xc6, 0x56, 0xf5, 0xc3, 0xc3, 0xf5, 0xe3, 0xe3]

for i in range(len(str)):
    print(chr((str[i]) >> 4 | (str[i]) << 4), end="")

실행 결과

Synchronize IDA Assembly

우리는 문제를 풀었지만, 더 성장하기 위해 조금 더 생각하고 공부해볼 것이다.

도구가 해주는 디컴파일로 문제를 쉽게 풀었지만, 실제 어떤 어셈블리어들이 사용되면서 연산되는지 비교해보자.

IDA에서는 디컴파일 코드와 실제 어셈블리어 코드를 sync하여 현위치를 알 수 있다. 오른쪽 버튼을 클릭하고 Synchronize with 메뉴로 연결시켜준다.

이후 IDA View를 나타내는 그래프를 끌어서 화면 오른쪽이나 왼쪽에 적절히 배치한다.

그림처럼 왼쪽에는 디컴파일된 코드, 오른쪽에는 어셈블리어로된 그래프가 나타난다. 이를 통해 어느 부분이 수행되는지 확인 가능하다.

for문 안의 if 구문의 어셈블리어 코드이다. 주요하게 볼 곳은 sar명령어, shl명령어 부분이다.

두 명령어 모두 시프트 연산자이고, sar은 오른쪽 시프트 연산자, shl은 왼쪽 시프트 연산자이다.

먼저, 14000102d에서 어디선가 값을 가져와서 eax에 저장하고 이 값을 오른쪽으로 4칸 시프트 연산한다. 이후, 14000104d에서 아까와 같은 값을 가져와서 ecx에 저장하고 왼쪽으로 4칸 시프트 연산한다.

ecx는 여기서 끝이 아니라 0xF0과 and연산을 수행한다. 그러면 ecx의 값의 하위 4비트는 0으로 고정된다(aaaa 0000). 또한 eax는 오른쪽으로 4칸 시프트했으므로, 상위 4비트는 0으로 고정된다(0000 bbbb).

이후, eax(0000 bbbb)와 ecx(aaaa 0000)를 or연산 한다. 연산 결과는 (aaaa bbbb)가 된다. 이 결과가 byte_140003000[i]와 일치해야 한다.

💡
IDA에서는 이 0xf0의 and연산을 생략했다. IDA가 똑똑해서 자료형의 길이를 인지하고 자동으로 생략한 것인지, 아니면 디컴파일에서 오류가 발생한지는 모르겠다. 하지만, IDA가 완벽한 디컴파일을 한다고 믿어서는 안된다. 어셈블리어 명령어도 공부해서 비교하면서 검증하면 실력이 더 늘 것 같다.

문제를 푸는 것도 중요하지만, 디테일하게 이해하고 가는 것이 좋으니까 길게 작성해봤다.

다음은 어셈블리어 코드를 생각하고 푼 파이썬 코드이다.

파이썬 풀이 코드

str = [0x24, 0x27, 0x13, 0xc6, 0xc6, 0x13, 0x16, 0xe6, 0x47, 0xf5, 0x26, 0x96, 0x47, 0xf5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xc6, 0x56, 0xf5, 0xc3, 0xc3, 0xf5, 0xe3, 0xe3]

for i in range(len(str)):
    eax = ecx = str[i]
    print(chr((ecx & 0xf0) >> 4 | (eax & 0x0f) << 4), end="")

실행 결과


Uploaded by N2T

    'Reversing' 카테고리의 다른 글
    • [Dreamhack] rev-basic-7 write up
    • [Dreamhack] rev-basic-6 write up
    • [Dreamhack] rev-basic-5 write up
    • [Dreamhack] rev-basic-3 write up
    e_yejun
    e_yejun
    정리노트 •_•

    티스토리툴바