본문 바로가기

DreamHack: System Hacking/F Stage 2

Quiz: x86 Assembly 2

문제: 다음 어셈블리 코드를 실행했을 때 출력되는 결과로 올바른 것은?
[Code]
main:
    push rbp
    mov rbp, rsp
    mov esi, 0xf
    mov rdi, 0x400500
    call 0x400497 <write_n>
    mov eax, 0x0
    pop rbp
    ret
    
write_n:
    push rbp
    mov rbp, rsp
    mov QWORD PTR [rbp-0x8],rdi
    mov DWORD PTR [rbp-0xc],esi
    xor rdx, rdx
    mov edx, DWORD PTR [rbp-0xc]
    mov rsi,QWORD PTR [rbp-0x8]
    mov rdi, 0x1
    mov rax, 0x1
    syscall
    pop rbp
    ret
    
==================================    
[Memory]
0x400500 | 0x3037207964343372
0x400508 | 0x003f367562336420

 

*풀이: 

main:
    push rbp
    mov rbp, rsp
은 메인함수의 함수 프롤로그에 해당한다

    mov esi, 0xf
    mov rdi, 0x400500
에서 esi에는 0xf라는 값이 저장되고, rdi에는 0x400500이라는 값이 저장된다

    call 0x400497 <write_n>
에서 write_n이라는 함수가 호출되어, 0x400497에 위치한 프로시저가 호출된다

write_n:
    push rbp
    mov rbp, rsp
는 write_n 함수의 함수 프롤로그에 해당한다.

    mov QWORD PTR [rbp-0x8],rdi
    mov DWORD PTR [rbp-0xc],esi
    xor rdx, rdx
에서 [rbp-0x8]에 rdi에 들어있는 값인 0x400500이 저장되고,
[rbp-0xc]에는 esi에 들어있는 값인 0xf가 저장되며
rdx는 0으로 초기화된다. (여기서 QWORD 크기를 저장하기 위해서 8byte만큼 rbp가 내려갔음을 확인할 수 있고,
DWORD를 저장하기 위해선 4byte만큼의 공간이 할당되었음을 확인할 수 있다.)

현재까지의 저장된 값들을 정리해보면 다음과 같다.
esi == 0xf
rdi == 0x400500
[rbp-0x8] == 0x400500
[rbp-0xc] == 0xf
rdx == 0

다시 코드로 돌아와서,

    mov edx, DWORD PTR [rbp-0xc]
    mov rsi,QWORD PTR [rbp-0x8]
    mov rdi, 0x1
    mov rax, 0x1
    syscall
에서 edx에는 0xf가, rsi에는 0x400500이 저장된다. 그리고 rax에 0x1이, rdi에는 0x1이 저장되며 syscall을 요청하게 된다.
rax=0x1이므로 write 시스템콜을 요청하게 되고, 
rdi, rsi, edx가 0x1, 0x400500, 0xf이므로
커널은 write(0x1(stdout), 0x400500(출력할 데이터의 주소값), 0xf(문자열의 길이))를 수행하게된다.

0x400500에는 다음과 같은 데이터가 들어있다
0x400500 | 0x3037207964343372
0x400508 | 0x003f367562336420

이를 1바이트 단위로 끊어보면
0x400500 | 0x30/37/20/79/64/34/33/72
0x400508 | 0x00/3f/36/75/62/33/64/20

0x72, 0x33, 0x34, 0x64, 0x79, 0x20, 0x37, 0x30
0x20, 0x64, 0x33, 0x62, 0x75, 0x36, 0x3f, 0x00이다. 여기서 길이가 0xf로 정해져있으므로

'0x72, 0x33, 0x34, 0x64, 0x79, 0x20, 0x37, 0x30
0x20, 0x64, 0x33, 0x62, 0x75, 0x36, 0x3f'까지만 출력받게 된다.

이를 ASCII 코드표로 치환하면 다음과 같다.
r34dy 70 d3bu6?

이후, write_n 함수는 끝나게 되고
메인 함수로 돌아와, leave(pop rbp) -> ret하여 메인 함수 역시 종료된다.