본문 바로가기

드림핵

[드림핵 로드맵] System Hacking - Quiz: x86 Assembly 3

<문제>

 

<풀이>

먼저 main함수의 코드를 보면

push rbpmov rbp, rsp는 스택 프레임을 생성하는 부분이므로 중요하게 생각하지 않아도 될 것 같고,

5행에서 write_n 함수를 호출하는 부분이 눈에 띈다.

 

write_n 함수의 코드에서는 syscall 명령어에 주목해보자.

드림핵 로드맵에서 x64 syscall 테이블을 가져와봤다.

rax, rdi, rsi, rdx에 값을 세팅해놓은 뒤 syscall 명령어를 실행하면 세팅한대로 시스템콜 요청이 이루어진다.

rax, rdi, rsi, rdx이 어떻게 세팅되었는지 문제 코드에서 살펴보자.

 

1. write_n함수의 9행에서 mov rax, 0x1을 확인할 수 있다. rax에는 0x1이 세팅되어있고, 이는 write 시스템콜을 요청한다는 의미이다.

 

2. write_n함수의 8행에서는 mov rdi, 0x1을 확인할 수 있다. rdiunsigned int fd로, 파일디스크립터를 지정한다. 파일디스크립터 0x1은 stdout으로 표준출력에 해당한다.

 

3. write_n함수의 7행에서는 mov rsi, QWORD PTR [rbp-0x8]을 확인할 수 있다. write_n함수의 3행에서 QWORD PTR [rbp-0x8]에는 rdi의 값이 저장된 것을 확인할 수 있고, main 함수의 4행에서 rdi에는 0x400500이 저장되어있었음을 확인할 수 있다. 결과적으로 rsi에는 0x400500이 세팅된다. rsi는 const char *buf이므로 0x400500에 있는 문자열을 가리키는 포인터 역할을 한다.

 

4. write_n함수의 5,6행에서는 우선 xor rdx,rdx로 rdx를 0으로 초기화한 뒤, mov edx, DWORD PTR[rbp-0xc]를 수행하는 것을 확인할 수 있다.  write_n함수의 4행에서 DWORD PTR[rdp-0xc]에는 esi의 값이 저장되는 것을 확인할 수 있고, main함수의 3행에서 esi에는 0xf가 저장되어있었음을 확인할 수 있다. 결과적으로 rdx에는 0xf(십진수로 15)가 세팅된다. rdx는 size_t count이므로 이를 통해 데이터의 크기를 15 바이트로 지정하게 된다.

 

따라서 rax, rdi, rsi, rdx가 이와 같이 세팅된 상태에서 syscall 명령어를 실행하면,

0x400500에 있는 데이터를 15바이트만큼 표준출력을 통해 write 시스템콜 요청하게 된다.

 

결국 그냥 메모리에 있는 데이터를 그대로 출력하는 것이구나.. 하고 출력하는 코드를 짜보았는데

chr_list = [
    0x30, 0x37, 0x20, 0x79, 0x64, 0x34, 0x33, 0x72,
    0x00, 0x3f, 0x36, 0x75, 0x62, 0x33, 0x64
    ]

for ch in chr_list:
    print(chr(ch), end='')

이렇게 하니까 07 yd43r?6ub3d 가 출력되었다.

 

문제는 리틀엔디안이었다.

x86아키텍처는 리틀엔디안 방식으로 문자열을 읽는다. 리틀엔디안은 낮은 주소에 데이터의 낮은 바이트를 저장한다.

쉽게 예를 들자면 "안녕하세요"를 "요세하녕안"으로 저장한다고 생각하면 된다.

그래서 코드를 아래와 같이 수정했다.

chr_list = [
    0x72, 0x33, 0x34, 0x64, 0x79, 0x20, 0x37, 0x30,
    0x20, 0x64, 0x33, 0x62, 0x75, 0x36, 0x3f
    ]

for ch in chr_list:
    print(chr(ch), end='')

r34dy 70 d3bu6?가 출력되었다.