00000000000011a9 <test>:
11a9: f3 0f 1e fa endbr64
11ad: 55 push %rbp
11ae: 48 89 e5 mov %rsp,%rbp
11b1: 89 7d fc mov %edi,-0x4(%rbp)
11b4: 83 7d fc 64 cmpl $0x64,-0x4(%rbp)
11b8: 75 07 jne 11c1 <test+0x18>
11ba: b8 01 00 00 00 mov $0x1,%eax
11bf: eb 05 jmp 11c6 <test+0x1d>
11c1: b8 00 00 00 00 mov $0x0,%eax
11c6: 5d pop %rbp
11c7: c3 retq
00000000000011c8 <win>:
11c8: f3 0f 1e fa endbr64
11cc: 55 push %rbp
11cd: 48 89 e5 mov %rsp,%rbp
11d0: 48 83 ec 20 sub $0x20,%rsp
11d4: 89 7d fc mov %edi,-0x4(%rbp)
11d7: 89 75 f8 mov %esi,-0x8(%rbp)
11da: 89 55 f4 mov %edx,-0xc(%rbp)
11dd: 89 4d f0 mov %ecx,-0x10(%rbp)
11e0: 44 89 45 ec mov %r8d,-0x14(%rbp)
11e4: 48 8d 3d 19 0e 00 00 lea 0xe19(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
11eb: e8 90 fe ff ff callq 1080 <puts@plt>
11f0: 90 nop
11f1: c9 leaveq
11f2: c3 retq
00000000000011f3 <lose>:
11f3: f3 0f 1e fa endbr64
11f7: 55 push %rbp
11f8: 48 89 e5 mov %rsp,%rbp
11fb: 48 8d 3d 07 0e 00 00 lea 0xe07(%rip),%rdi # 2009 <_IO_stdin_used+0x9>
1202: e8 79 fe ff ff callq 1080 <puts@plt>
1207: 90 nop
1208: 5d pop %rbp
1209: c3 retq
000000000000120a <main>:
120a: f3 0f 1e fa endbr64
120e: 55 push %rbp
120f: 48 89 e5 mov %rsp,%rbp
1212: 48 83 ec 10 sub $0x10,%rsp
1216: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
121d: 00 00
121f: 48 89 45 f8 mov %rax,-0x8(%rbp)
1223: 31 c0 xor %eax,%eax
1225: 48 8d 3d e1 0d 00 00 lea 0xde1(%rip),%rdi # 200d <_IO_stdin_used+0xd>
122c: b8 00 00 00 00 mov $0x0,%eax
1231: e8 6a fe ff ff callq 10a0 <printf@plt>
1236: 48 8d 45 f0 lea -0x10(%rbp),%rax
123a: 48 89 c6 mov %rax,%rsi
123d: 48 8d 3d d6 0d 00 00 lea 0xdd6(%rip),%rdi # 201a <_IO_stdin_used+0x1a>
1244: b8 00 00 00 00 mov $0x0,%eax
1249: e8 62 fe ff ff callq 10b0 <__isoc99_scanf@plt>
124e: 8b 45 f0 mov -0x10(%rbp),%eax
1251: 89 c7 mov %eax,%edi
1253: e8 51 ff ff ff callq 11a9 <test>
1258: 89 45 f4 mov %eax,-0xc(%rbp)
125b: 83 7d f4 01 cmpl $0x1,-0xc(%rbp)
125f: 75 21 jne 1282 <main+0x78>
1261: 41 b8 05 00 00 00 mov $0x5,%r8d
1267: b9 04 00 00 00 mov $0x4,%ecx
126c: ba 03 00 00 00 mov $0x3,%edx
1271: be 02 00 00 00 mov $0x2,%esi
1276: bf 01 00 00 00 mov $0x1,%edi
127b: e8 48 ff ff ff callq 11c8 <win>
1280: eb 10 jmp 1292 <main+0x88>
1282: 83 7d f4 00 cmpl $0x0,-0xc(%rbp)
1286: 75 0a jne 1292 <main+0x88>
1288: b8 00 00 00 00 mov $0x0,%eax
128d: e8 61 ff ff ff callq 11f3 <lose>
1292: b8 00 00 00 00 mov $0x0,%eax
1297: 48 8b 55 f8 mov -0x8(%rbp),%rdx
129b: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
12a2: 00 00
12a4: 74 05 je 12ab <main+0xa1>
12a6: e8 e5 fd ff ff callq 1090 <__stack_chk_fail@plt>
12ab: c9 leaveq
12ac: c3 retq
12ad: 0f 1f 00 nopl (%rax)
push rbp (main+4)
mov rbp, rsp (main+5)
스택 프레임 생성
sub rsp, 0x10 (main+8)
rsp를 0x10만큼 빼서 스택 프레임의 크기를 늘림
mov rax, qword ptr fs:[0x28] (main+12)
보호됨
mov qword ptr [rbp - 8], rax (main+21)
스택의 rbp - 8 부분에 rax 저장
xor eax, eax (main+25)
eax의 값을 0으로 만든다.
mov eax, 0보다 xor eax, eax나 sub eax, eax가 더 작기 때문에 쓰는것 같다.
lea rdi, [rip + 0xde1] (main+27)
rdi에 [rip + 0xde1]의 주소를 넣는데 이 위치엔 "Enter Num : " 문자열이 위치해 있다.
mov eax, 0 (main+34)
함수를 호출하기 전에 반환값을 0으로 초기화 해 주는거 같다.
call printf@plt (main+39)
printf 함수를 호출한다.
lea rax, [rbp - 0x10] (main+44)
mov rsi, rax (main+48)
rsi: 시스템 V AMD64 ABI 기준 함수의 2번째 인자
lea rdi, [rip + 0xdd6]
rdi: 시스템 V AMD64 ABI 기준 함수의 1번째 인자
mov eax, 0
반환값 0으로 초기화
call __isoc99_scanf@plt
scanf 함수 호출, 일단 5984 넣어보겠다.
이걸 C 코드로 바꿔서 생각을 해 보면
int x;
scanf("%d", &x);
가 되겠다.
int x가 위치한 스택에서의 위치는 rbp - 0x10이 되겠다.
mov eax, dword ptr [rbp - 0x10]
rbp - 0x10은 아까 위에 c 코드로 보면 int x가 위치한 주소고, eax는 그냥 레지스터다.
eax에 scanf에서 입력받은 값을 저장한다.
mov edi, eax
edi가 첫번째 인자이고 아래에 함수가 오므로 아래 함수의 첫번째 인자에 eax의 값을 넣는다.
call test
test 함수를 호출한다. si 명령어를 통해서 내부로 들어가보겠다.
push rbp (test+4)
mov rbp, rsp (test+5)
스택 프레임을 생성해주는 기본적인 코드다. (함수 프롤로그)
mov dword ptr [rbp - 4], edi (test+8)
첫번째 인자를 스택에 저장한다. (레지스터는 다른곳에서도 많이 써야 하기 때문에)
cmp dword ptr [rbp - 4], 0x64 (test+11)
jne test+24 (test+15)
분기문이 나왔다.
cmp dword ptr [rbp - 4], 0x64
rbp - 4와 0x64를 비교한다.
jne test+24
rbp - 4가 0x64가 아니면 test+24로 점프한다.
일단 인자로 들어온 값(rbp - 4)가 0x64가 아니라고 가정하고 생각을 해보겠다.
jne test+24 (test+15)
mov eax, 0 (test+24)
pop rbp (test+29)
ret (test+30)
mov eax, 0 을 해주고 바로 함수 에필로그가 실행된다.
pop rbp
ret
(함수 에필로그)
한번 c 코드로 만들어보자
int test(int x){
if (x != 0x64){
return 0;
}
...
}
이렇게 될 것이다.
그러면 만약 인자로 들어온 값이 0x64라고 생각을 하고 코드를 해석해보자
cmp dword ptr [rbp - 4], 0x64 (test+11)
jne test+24 (test+15) //점프 안함
mov eax, 1 (test+17)
jmp test+29 (test+22)
pop rbp (test+29)
ret (test+30)
mov eax, 1은 eax를 1로 설정해준다. (eax는 반환값을 저장하는 용도로도 사용되는 레지스터이다.)
jmp test+29는 test+29줄로 무조건 점프한다.
test+29로 점프했으니
pop rbp 부분부터 실행한다.
pop rbp
ret
함수의 에필로그이다.
C 코드로 한번 만들어보자
int test(int x){
if (x != 0x64){
...
}
else{
return 1;
}
}
이렇게 될 것이다.
한번 두 코드를 합쳐보면
int test(int x){
if (x != 0x64){
return 0;
}
else{
return 1;
}
}
이렇게 될 것이다.
그러면 test 함수는 분석을 다 했다.
위에서 함수 분석을 한번 해봤으니까 두번째부턴 간단히 하고 넘어가겠다.
mov dword ptr [rbp - 0xc], eax (main+78)
eax(반환값)을 rbp - 0xc 위치에 저장한다.
cmp dword ptr [rbp - 0xc], 1 (main+81)
jne main+120 (main+85)
[rbp - 0xc]와 1을 비교해서
[rbp - 0xc]가 1이 아니면 main+120으로 점프한다.
rbp - 0xc가 1일때
main+120로 넘어가지 않고 계속 진행한다
mov r8d, 5
mov ecx, 4
mov edx, 3
mov esi, 2
mov edi, 1
인자 순서가
edi -> esi -> edx -> ecx -> r8 -> r9 -> 스택
순이므로
아래에 오는 함수의 인자에
(1, 2, 3, 4, 5) 순서대로 넣는다.
call win
win 함수를 호출한다.

push rbp
mov rbp, rsp
함수 프롤로그
sub rsp, 0x20
스택 프레임에 0x20만큼 공간 생성
mov dword ptr [rbp - 4], edi
mov dword ptr [rbp - 8], esi
mov dword ptr [rbp - 0xc], edx
mov dword ptr [rbp - 0x10], ecx
mov dword ptr [rbp - 0x14], r8d
함수의 인자들을 차례대로 스택에 넣어준다.
lea rdi, [rip + 0xe19]
rip + 0xe19의 주소를 계산해서 rdi에 넣어주는데, 이 값은 "good" 이다.
그리고 rdi는 함수의 첫번째 인자에 사용되는 레지스터이다.

call puts@plt
puts 함수를 실행한다. puts 함수가 뭘 하는진 알거라고 생각한다.
nop
파이프라인에서 한 사이클 쉬라는 뜻이다.
leave
현재까지 썻던 메모리 스택을 깔끔히 비우고,
자신을 호출했던 메모리의 베이스 주소를 ebp에 다시 채운다.
ret
함수 종료
eax를 설정하지 않는걸 보아 아마도 이 함수의 반환형은 void일 것이다.
jmp main+136 (main+118)
main+136 위치로 무조건 점프한다.
rbp - 0xc가 1이 아닐때
main+120으로 점프해 main+120부터 실행한다.
cmp dword ptr [rbp - 0xc], 0 (main+120)
jne main+136 (main+124)
rbp - 0xc가 0이 아니면 136번줄로 이동한다.
test 함수에서는 0과 1만 반환하기 때문에 메모리에 오류가 생긴게 아닌 이상 이 분기를 탈 일은 없다.
mov eax, 0
함수의 반환값을 0으로 초기화 한다.
call lose
lose 함수를 호출한다.

push rbp
mov rbp rsp
함수 프롤로그다.
lea rdi, [rip + 0xe07]
rdi(첫번째 인자) 에 [rip + 0xe07]의 주소를 넣는다.

nop
파이프라인에서 한 사이클 쉰다.
pop rbp
ret
함수 에필로그
eax를 설정하지 않는걸 보아 아마도 이 함수의 반환형은 void일 것이다.
mov eax, 0
함수의 반환값을 0으로 설정한다.
mov rdx, qword ptr [rbp - 8]
rbp - 8의 있는 값을 rdx로 옮긴다.
정확히 무슨 값인진 잘 모르겠지만 아마도 canary 값으로 보인다.
(스택 오버플로우가 일어났는지 검증하기 위한 값)
아래 __stack_chk_fail 함수에 필요한 인자인것 같다.
xor rdx, qword ptr fs:[0x28]
보호된 코드
je main+161
main+161로 점프한다.
call __stack_chk_fail@plt
스택 오버플로우가 났을시에 실행되는 함수인것 같다.
leave
ret
함수 에필로그
이 전체 코드를 하나의 c 코드로 바꿔보자면
#include <stdio.h>
int test(int x){
if (x != 0x64){
return 0;
}
else{
return 1;
}
}
void win(int a, int b, int c, int d, int e){
puts("good");
}
void lose(){
puts("bad");
}
int main(){
int x;
printf("Enter Num : ");
scanf("%d", &x);
int c = test(x);
if (c == 1){
win(1, 2, 3, 4, 5);
}
else if (c == 0){
lose();
}
return 0;
}
이렇게 될 것 이다.
그러면 good 이 나오게 하고 싶으면 0x64에 해당되는 정수 100을 넣으면 될 것이고,
bad가 나오게 하고 싶으면 100이 아닌 다른 수를 넣으면 될 것 같다.