[Register]
rcx = 0
rdx = 0
rsi = 0x400000
-------------------------
[Memory]
0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00
-------------------------
[code]
1: mov dl, BYTE PTR[rsi+rcx]
2: xor dl, 0x30
3: mov BYTE PTR[rsi+rcx], dl
4: inc rcx
5: cmp rcx, 0x19
6: jg end
7: jmp 1
첫번째 줄은 rsi+rcx에 있는 값을 dl에 대입합니다. 현재 rsi는 0x400000이고 rcx는 0이므로 0x400000에서 1바이트만큼 참조하면 dl이 0x67이 됩니다.
두번째 줄은 dl과 0x30을 xor 연산 합니다. 현재 dl에 저장된 값은 0x67이므로 xor 0x67, 0x30을 하면 0x57이 됩니다.
세번째 줄은 dl의 값을 rsi+rcx에 저장합니다. 여기까지 실행한 메모리 구조는 다음과 같습니다.
[Memory]
0x400000 | 0x57 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00
0x400000의 데이터가 0x67에서 0x57로 변경된 것을 확인할 수 있습니다.
4번째 줄은 rcx에 1을 더합니다.
다섯번째 줄은 rcx와 0x19를 비교하고, 여섯번째 줄에서 rcx가 만약 0x19보다 더 크다면 프로그램을 종료하게 됩니다.
만약 프로그램이 종료되지 않았다면 일곱번째 줄에 의해 첫번째 줄로 돌아갑니다.
처음으로 돌아오게 되면 dl에 대입할 값의 주소가 rcx의 값이 늘어나면서 변하게 됩니다. 한 번 반복하면 rcx가 1이므로 0x400001의 값을 참조하고, 두 번 반복하면 rcx가 2이므로 0x400002의 값을 참조하게 됩니다.
그리고 rcx가 0x20이 되면 프로그램이 종료하기 때문에 이 프로그램은 rcx의 값이 0부터 19까지 1씩 증가하면서 반복하게 됩니다. 그 말은 0x400000부터 rcx가 19일 때인 0x400019까지의 데이터들을 0x30과 xor한 값으로 변경한 후 프로그램이 종료됩니다.
문제의 정답은 0x400000부터 0x400019까지의 데이터에 0x30을 각각 xor 연산하여 구하는 것으로 계산하면 Welcome to assembly world! 라는 답을 구할 수 있습니다.
[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에서는 esi에 0xf, rdi에 0x400500이라는 값을 저장한 후 write_n을 call합니다.
write_n에서 rdi 값을 스택에 저장한 후, esi 값도 스택에 저장합니다. 그 후 rdx를 0으로 만들고 edx에 esi값인 0xf를, rsi에 rdi값인 0x400500을 저장합니다. rdi, rax에 0x1을 저장하고 syscall을 실행합니다. Linux system call table을 참조하면 sys_write가 실행되는 것을 알 수 있고, rsi에서 rdi만큼의 크기를 출력합니다.
0x400500에서부터 16바이트를 출력해 보겠습니다.
먼저 0x400500을 바이트 단위로 나누면 0x30, 0x37, 0x20, 0x79, 0x64, 0x34, 0x33, 0x72이고 이를 아스키 코드로 바꾸면 "07 yd43r"이 되는 것 같지만 x86 아키텍처는 바이트 오더링 방식으로 리틀 엔디안을 사용하기 때문에 앞의 값을 뒤집은 문자열인 "r34dy 70" 이 되고 0x400508의 데이터도 똑같이 구하면 " d3bu6?"이 됩니다(마지막 0x00은 null). 따라서 정답은 두 문자열을 합친 "r34dy 70 d3bu6?"이 됩니다.