[리버싱] 3차시 과제2
main 0: rpb를 push한다.
main 1: rbp에 rsp를 저장한다.
<스택 프레임 형성>
main 4 : rsp의 값과 0x20를 뺀 후에 그 값을 rsp에 저장한다.
main 8: mov rax에 fs:0x28부터 QWORD만큼까지에 해당하는 값을 저장한다. 스택 카나리 기법이다.
main <+17>: rbp-0x8에서부터 QWORD만큼까지에 rax값을 저장한다.
main <+21>: eax와 eax를 xor연산한다. 따라서 0이다.
main <+23>: edi에 0xfeedc0de값을 저장한다. (0xfeedc0de로 최기화)
main <+28>: 0x400580에 있는 <srand@plt>함수를 호출한다.
main <+33>: rbp-0x10에서 DWORD까지에 0x0 저장한다.
main <+40>: 0x4007ca에 위치한 <main+111>함수로 점프한다.
main <+42>: edi에 0x4008d4을 저장한다.
main <+47>: 0x400550에 위치한 <puts@plt>함수를 호출한다.
main<+52>: 0x4005b0에 위치한 <rand@plt>함수를 호출한다.
main <+57>: rbp-0xc부터 DWORD만큼까지에 eax값을 저장다.
main <+60>: rax에 주소값 [rbp-0x14]이 유효하다면 저장한다.
main <+64>: rsi에 rax를 저장한다. rsi는 문자열의 시작주소를 저장하는 용도로 쓰인다.
main <+67>: edi에 0x4008ec값을 저장한다. rdi는 문자열의 마지막 주소를 저장하는 용도로 쓰인다.
main <+72>: eax에 0x0를 저장한다. 0x0는 10진수로 0이다.
main <+77>: 0x4005a0에 위치한 <__isoc99_scanf@plt>함수를 호출한다. scanf함수는 입력을 받는 함수이다.
main <+82>: mov eax,DWORD PTR [rbp-0x14]
main <+85>: cmp [rbp-0xc]부터 DWORD(4byte)만큼에 해당하는 값과 eax의 값을 빼서 대소관계, 혹은 같은지를 판단한다. 또 이에 맞게 ZF와 CF를 설정한다. ZF는 두 값이 같을 때 1이 되고, CF는 두 값을 뺐을때 자리내림이 발생하면 1이 된다.
main <+88>: 위에서 cmp로 비교한 값이 같을 때, 즉 ZF가 1일 때 0x4007c6에 위치한 <main+107>로 점프한다.
main <+90>: edi에 0x4008ef을 저장한다.
main<+95>: 0x400550에 위치한 <puts@plt>함수를 호출한다. puts함수는 문자열을 출력하는 함수이다.
main <+100>: eax에 0x1를 저장한다. 0x1은 10진수로 1이다.
main <+105>: 0x40082c에 위치한 <main+209>로 점프한다.
main <+107>: rbp-0x10부터 DWORD만큼에(4byte) 해당되는 값에 0x1을 더한다.
main <+111>: rbp-0x10부터 DWORD만큼(4byte)까지에 해당되는 값과 0x13을 빼서 큰지, 작은지, 같은지에 따라서 ZF와 CF를 설정한다. 두 값이 같다면 ZF가 1이 되고, 자리내림이 발생한다면(빼는 것이므로 자리올림은 생각하지 않아도 된다)CF가 1이 된다. 0x13은 10진수로 19이다.
main <+115>: 위에 cmp로 비교한 결과가 rbp-0x10부터 DWORD만큼에 해당되는 값이 0x13보다 작거나 같을 경우에 0x400785에 위치한 <main+42>로 점프한다.
<반복문이다 (for)>
main <+117>: edi에 0x4008f8를 저장한다.
main <+122>: 0x400550에 위치한 <puts@plt>함수를 호출한다. puts함수는 문자열을 출력하는 함수이다.
main <+127>: rax에 [rbp-0x14]가 유효한 주소라면, 이 주소를 저장한다.
main <+131>: rsi에 rax값을 저장한다.
main <+134>: edi에 0x4008ec값을 저장한다.
main <+139>: eax에 0x0값을 저장한다. 0x0은 10진수로 0이다.
main <+144>: 0x4005a0에 위치한 <__isoc99_scanf@plt>함수를 호출한다. scanf는 입력을 받는 함수이다.
main <+149>: eax에 rbp-0x14부터 DWORD만큼에 해당되는 값을 저장한다.
main <+152>: eax에서 0xdeadface를 빼서 대소관계와 같은지를 판단한다. 그리고 이를 바탕으로 ZF와 CF를 설정하는데
두 값을 뺀 값이 0되서, 두 값이 같다는 결과가 나오면 ZF는 1이 되고 두 값을 뺐는데 자리올림이나 자리내림이 발생하면 CF가 1이된다.
main <+157>: 위에서 cmp한 값이 0이 아니라면, 즉 두 값이 서로 다르다면 0x40081d에 위치한 <main+194>로 점프한다.
main <+159>: edi에 0x40090d를 저장한다.
main <+164>: 0x400550에 위치한 <puts@plt>함수를 호출한다. puts는 문자열을 출력하는 함수이다.
main <+169>: eax에 0x0값을 저장한다. 0x0는 10진수로 0이다.
main <+174>: 0x4006c6에 위치한 <flag_generator>함수를 호출한다.
main <+179>: rdi에 rax값을 저장한다. rdi는 문자열의 끝 주소를 저장하는 용도로 주로 쓰인다.
main <+182>: 0x400550에 위치한 <puts@plt>함수를 호출한다. puts는 문자열을 출력하는 함수이다.
main <+187>: eax에 0x0를 저장한다. 0x0는 10진수로 0이다. eax와 rax의 차이는, eax는 32bit이고 rax는 64bit라는 점이다.
main <+192>: 0x40082c에 위치한 <main+209>로 점프한다.
main <+194>: edi에 0x400916값을 저장한다. edi는 32bit 레지스터이고 문자열의 끝(마지막) 주소를 저장하는 용도로 주로 쓰인다.
main <+199>: 0x400550에 위치한 <puts@plt>함수를 호출한다. puts는 문자열을 출력하는 함수이다.
main <+204>: eax에 0x1값을 저장한다. 0x1은 10진수로 1이다.
main <+209>: rdx에 주소 rbp-0x8에서 QWORD(8byte)만큼에 해당되는 값을 저장한다.
main <+213>: rdx와 주소 fs:0x28부터 QWORD만큼까지에 해당되는 값을 xor연산한다.
main <+222>: 위 연산의 결과가 두 값이 같다로 나오면 0x400840에 위치한 <main+229>로 이동한다.
main <+224>: 0x400560에 위치한 <__stack_chk_fail@plt>함수를 호출한다.
main <+229>: mov esp, ebp와 pop ebp를 합친 명령어로, ebp를 esp에 옮기고 ebp를 pop한다.
main <+230>: pop eip와 jmp eip를 합친 명령어로, 다음 명령어에 해당하는 eip를 pop하고 eip에는 ret 주소가 들어간다.
그 다음 그 주소로 점프한다. 즉, 함수가 끝나고 원래 시작된 주소로 돌아가는 것이다.
*랜덤함수의 시드를 설정하고, 랜덤함수에서 나온 값과 입력받은 값을 비교하고 틀리면 209번째 줄로 이동하는데, 이는 함수 에필로그로 함수가 종료되는, 즉 프로그램이 끝나는 부분이다.
*지금 한 것은 정적분석인데, 이 문제의 경우는 동적분석을 할 때 이점이 있다. flag_generator함수를 호출하면 flag를 볼 수 있기 때문이다.
*3735943886은 int의 최대 크기인 21억을 넘어갔으므로 unsigned로 넣든, signed -으로 넣든 오버플로우가 나서 똑같다.
이 문제는 srand와 rand의 취약점을 이용해서 정해진 값을 맞추는 것이 핵심인 문제이다.
srand와 rand의 취약점이란, 간단히 말해서 시드의 값을 정해놓으면 랜덤값이 일정하게 나오는 취약점이다.
그리고 시드가 0xfeedc0de라는 것을 알았으니 반복문을 이용해서 이 시드에서 나올 수 있는 20개의 값들을 하나씩 대입해서 모두 맞추고(브루트포스), 마지막으로 0xdeadface를 10진수로 변환한 값을 입력하면 flag를 얻을 수 있다.
prob2를 실행시키면 위처럼 랜덤값을 입력할 수 있고, 잘못된 값을 입력하면 Wrong!!!이 뜬다.
시드를 설정하고 반복문을 통해 구한 20개의 값은 다음과 같다.
이 값들을 하나씩 모두 대입해보면,
마지막으로 input correct number라는 문장이 출력되면서 입력을 받는다.
이때 0xdeadface를 10진수로 고친 3735943886을 입력하면,
flag가 출력되는 것을 확인할 수 있다.
flag는 Layer7{Y0u_are_m45ter_0f_A55EMB1y!} 였다.