Layer7 과제/리버싱

[리버싱] 3차시 과제2

kms0204 2022. 7. 27. 22:05

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!} 였다.