다운로드 후에 실행시켜 보면 

 

 

이러한 문자열이 나오는데...

IDA로 한번 확인을 해보았다.

 

 

1. v5는 sup의 시작 주소 

2. fgets 함수는 133 바이트를 받음 

 

자 그러면  sup의 함수는 무슨 함수인지 보면 

 

 

그냥  s를 출력해준다. 

여기서 알 수 있는 정보는 v5는 sup의 시작 주소인것을 알 수 있다.

 

 

v5 = 08484B4 

 

 

함수 목록을 찾아보면 shell이 보인다.

그래서 shell을 실행하는 것으로 추측하고 한번 확인해 보았다.

 

 

역시나 쉘을 사용하는 함수이다.

 

 

shell = 0804849B

 

여기서 생각해볼 수 있는것은 지금 v5는 sup의 시작 주소이고

sup 함수는 그냥 입력받은 s를 출력하고 있기 때문에 

v5의 함수의 값을 변조 할 수 없다. 

그렇다면 shell을 v5에 덮어씌운다면 ??

 

 

 

v5는 sup의 시작주소이고 sup은 s를 출력해주고 있으니까 v5는 s의 값이 된다.

각각의 차이가 128byte 차이가 나는데 ida에서 확인했을때는 

fgets 함수가 133byte를 받아온다. 

 

그래서 페이로드를 작성해보면 다음과 같다.

 

Payload.py

 

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3001)

payload = "A"*128 + p32(0x0804849b)

p.sendline(payload)
p.interactive()

 

 

'CTF' 카테고리의 다른 글

Hack CTF x64 Buffer Overflow  (0) 2021.09.25
Hack CTF - 내 버퍼가 흘러넘친다  (0) 2021.09.25
Pwnable.kr 1 - fd  (0) 2021.09.24
Hack CTF Basic_FSB  (0) 2021.09.23
Hack CTF basic bof#1  (0) 2021.09.22

 

해당 파일을 다운로드 해야하는데 

일단 리눅스의 nc를 통해서 리눅스에서 실행해보면 

 

 

a를 많이 입력을 해보면 해당 문구가 출력되는것을 볼 수 있는데

bof를 이용한 문제인것 같다.

 

그러면 다시 윈도우로 돌아와서 파일을 다운로드하고 소스코드를

확인한다면 뭔가 단서가 있을 것이다.

 

IDA를 사용해서 한번 확인을 해보았다.

디컴파일한 결과를 볼려면 F5를 하면 된다.

 

 

15번 째 라인을 보면 쉘을 실행시키는 시스템 함수가 들어가 있는 것을 확인

할 수 있었고, 그러기 위해서 12번째 라인의 조건문에 만족하는 조건으로

변경해줘야 한다는 것을 볼 수 있다.

 

v5가 -559038737이면 system 함수를 이용해서 flag를 보여주는것 같다.

그러면 v5의 값을 -559038737로 바꿔줘야 하는것 같다.

 

 

위의 코드를 입력해서 16진수로 변환을 해보았다.

 

 

해당 문자열이 보이는데 그러면 16진수이기 때문에0xdeadbeef로 변환이 되는데

이것은 -559038737이다. -> 아까 봤던 ida로 가보자 

 

 

여기서 fgets라는 함수가 45의 크기를 받는다.

그러면 pwntools로 코드를 작성해보자 

 

from pwn import*

p = remote("ctf.j0n9hyun.xyz",3000)
v5 = 0xdeadbeef

payload = "a"*40 + p32(v5)

p.sendline(payload)
p.interactive()

 

 

이렇게 flag가 나오는것을 확인 할 수 있다.

 

 

성공!!

'CTF' 카테고리의 다른 글

Hack CTF x64 Buffer Overflow  (0) 2021.09.25
Hack CTF - 내 버퍼가 흘러넘친다  (0) 2021.09.25
Pwnable.kr 1 - fd  (0) 2021.09.24
Hack CTF Basic_FSB  (0) 2021.09.23
Hack CTF basic Bof #2  (0) 2021.09.22

 

위의 소스코드를 한번 보도록 합시다

 

 

위의 코드들로 한번 pwntools를 작성해 보면 

 

 

13개의 문자까지만 출력이 되는 것을 볼 수 있는데, 

왜냐하면 우리가 p.recv(13)을 통해서 13자 까지만 받아오도록 

코드를 작성 하였기 때문입니다.

 

그러면 조금 더 응용을 해볼까요?

 

 

해당 코드를 작성을하고 파이썬 소스를 한번 만들어 봅시다. 

 

 

여기서 이렇게 작성을 해준 다음에 실행을 해보면

한줄만 나오는 것을 확인 할 수 있습니다. -> 한줄을 받아오기 때문

 

 

그렇다면 여기서 한번더 응용을 해보면 

특정 문자열이 나올때 까지 한번 출력을 받아오는 것은 가능한지 알아보겠습니다.

그렇게 하기 위해서는 당연히 파이썬의 pwntools 소스코드를 수정을 해주어야 되겠죠?

 

 

이렇게 수정을 해주면 'pwn' 이라는 문자열을 만날때 까지 출력을  하니까

그러면 hello world와 hello pwn까지 출력이 되는지 확인을 해 봅시다.

 

 

우리가 예상했던것과 마찬가지로 잘 나오는 것을 확인 할 수 있습니다.

이렇게 우리가 input과 output을 한번 알아보았습니다.

 

다음은 packing입니다.

pwntools를 이용해서 패킹과 언패킹을 진행해보도록 하겠습니다.

 

 

 

리틀엔디언 바이트 값으로 패킹이 된 것을 확인 할 수 있습니다.

그럼 이번에 반대로 해볼까요??

 

 

 

이렇게 반대로 언패킹되는것을 볼 수 있습니다.

이렇게 pwntools에 대해서 알아보았습니다

이번에는 Pwntools 소개를 한번 해볼까 합니다

왜냐하면 input, 패킹, 언패킹, 바이너리 내부 함수 주소들을 자동으로 가져오는 등의

기능을 제공하여 보다 쉽게 분석이 가능하기 때문입니다.

그럼 이걸왜 이제 소개하냐!

앞으로 진행할 exploit 도 그렇고 아래와 같이 불편하게 input을 넣지 않고 

편하게 output을 받아오기 위해서 알아둘 필요가 있다고 생각합니다.

 

(python -c "print 'A'*0x30 + 'B'*0x8 + '\xa7\x05\x40\x00\x00\x00\x00\x00'";cat)| ./binary

 

실제 워게임이나 ctf에서도 자주 사용하는 라이브러리 입니다.

 

 

위는  scoket 라이브러리 사용 코드 입니다

하지만 이것을 pwntools 라이브러리를 사용하면 훨씬 간결하게 아래와 같이 만들 수 있습니다.

 

 

간결 해진것을 볼 수 있습니다.

그럼 이제 pwntools 라이브러리를 로드 해보겠습니다.

 

 

설치방법은 아래의 링크를 참고해주시기 바랍니다.

 

https://juns0208.tistory.com/40

 

Pwntools- 설치및 사용법1

Pwntools 란? Pwntools는 말 그대로 포너블 툴 킷으로 익스플로잇을 편하게 하기 위해서 사용됩니다. 파이썬으로 작성되어 있으며 from pwn import *을 선언해서 사용할 수 있습니다. Gallopsled라는 팀에 의

juns0208.tistory.com

 

그러면 이제 local 바이너리를 연결 해보겠습니다

 

 

p = process ('./[바이너리 이름]')

 

연결이 된것을 확인 하였습니다.

그러면 이제 서버 바이너리를 연결 해보도록 하겠습니다.

 

 

p = remote('./address',port) #주소 + 포트 번호

 

연결이 된 것을 확인 할 수 있습니다.

 

그럼 여기서 연결된 서버 바이너리에 input을 넣고 output을 보겠습니다

먼저 예시 코드를 사용해서 보도록 합시다.

 

예시 코드 

 

#include <stdip.h>

int main()
{
	char arr[100];
    
    scnaf("%s",arr);
    
    printf("hello %s\n",arr);
    
    return 0;
}

 

단순히 입력을 받으면 해당 입력을 출력하는 c 소스입니다.

여기서 pwntools 라이브러리를 이용해서 input을 보내고 

그 input을 받아서 output이 나오는지 확인을 하면 됩니다.

gcc로 컴파일을 해주고 파이썬 소스코드를 제작 해봅시다.

 

from pwn import*
p = process('./a.out')
p.sendline('AAAA')
p.interactive()

 

 

정상적으로 우리가 보낸 입력값이 나오고 있는것을 볼 수 있습니다.

이번에는 output을 받아오겠습니다.

 

p = process('./a.out')
data = p.recv(13)

 

a.out이라는 바이너리가 최대 13바이트까지 받아서 data 변수에 저장을 하게 됩니다.

 

예제 코드 

 

#include<stdio.h>

int main(void)
{
	puts("hello pwnalble");
    
    return 0;
}

 

이제 무슨 코드인지는 대충 알것이라 생각하고 넘어가겠습니다.

 

Python code

 

from pwn import*

p = process('./a.out')
data = p.recv(13)
print(data)
p.interactive()

 

 

13바이트 만큼 받아서 출력이 됩니다.

 

이렇게 Pwntools를 알아보았습니다

다음 포스팅에서는 조금더 심화적으로 사용하는 방법을 알아보도록 하겠습니다.

func 의 함수를 gdb로 보겠습니다.

 

 

cmp 구문과 jle 구문 jmp 구문이 있는것으로 보아 반복문 이거나 아니면 조건문 같습니다.

하지만 +64로 jmp를 하는것을 보면 cmp로 비교를 하고 조건을 따라서 이동을 하고 있습니다.

조건문이 계속적으로 사용되고 있는것 같습니다. 

 

 

조건에 부합하면 +64로 이동하여 종효가 되고 조건이 맞지 않으면 다른 조건이 있는 구문으로 

이동을 하게 됩니다. 그래서 조건문이 여러개가 사용됬다는것을 유추 할 수 있습니다.

 

 

90을 입력하니까 A등급이 나오고 있습니다.

추측을 해보면 점수별로 등급을 알려주는 프로그램 인것 같습니다.

 

 

여기까지 func 함수를 알아보았습니다

gdb-peda를 이용해서 한번 main 함수를 봐야겠죠?

 

 

여기서 함수들이 사용이 되는것을 알 수 있는데 한번 보면 

printf 함수와 scanf함수, 다시 printf함수가 사용되는것을 볼 수 있습니다. 

(이제 자세한 설명은 생략하도록 하겠습니다.)

 

한줄 한줄 살펴봅시다. 

 

 

여기는 이제 바로 알겠죠? 스택 프롤로그 입니다. 

 

 

이렇게 rax의 값을 0으로 만들어 주고 있습니다.

 

 

그런다음 rbp-0xc라는 곳에 0이라는 값을 넣고 있습니다.

 

 

eax에 0이라는 값을 mov 해주고 있습니다.  그 후에 printf 함수를 call 하고 있네요 

한번 계속해서 실행 해보도록 하겠습니다.

 

 

하지만 printf 함수의 문자열이 출력이 될것이라고 생각했지만 결과는 아무것도 나오지 않았습니다.

왜 이렇게 되는걸까요? -> 추후에 설명하도록 하겠습니다.

scanf 함수를 call하는 +66까지 실행을 시켜보겠습니다.

 

 

정상적으로 실행되고 있습니다. 

 

 

계속 실행해보면 func함수를 호출하고 있습니다.

 

 

printf 함수를 호출하고 종료가 되는것 같습니다.

 

 

지금까지 메인함수를 보았습니다

다음 포스팅에서 func 함수를 마저 보도록 하겠습니다.

 

예정대로 func 함수를 살펴 보도록 하겠습니다.

 

 

gdb를 사용한다면 disass func라고 해도 되지만 peda를 사용하고 있기때문에 조금더 편리한

peda를 사용해서 한번 살펴보도록 합시다. peda로 확인을 하고 싶을땐 아래와 같이 입력하면 됩니다.

 

pd func //pd 함수이름

 

자 그러면 위에 처럼 어셈블리 코드가 또 나오게 되는데요

그러면 늘 했던거 처럼 breakpoint를 걸고 한 줄 한 줄 확인해 봅시다.

하지만 여기서 주의해야 할것은 우리가 늘 보던 main 함수가 아니기 때문에

break point를 main함수에 걸면 안됩니다. 그래서 또 하나의 방법인 예전에 포스팅에서

배운 숫자를 이용해서 break point를 걸어 보겠습니다.

 

 

break point를 걸었지만 실행을 해보면 메모리에 접근을 할수 없다고 나오네요 

그러면 코드가 길지는 않은거 같으니 정적으로 분석을 해야 할 것 같습니다.

 

 

당연히 맨처음에는 스택 프롤로그를 볼 수 있습니다.

 

 

다음에는 edi의 값을 rbp-0x14의 위치에 넣어주고 rbp-0x4 주소에 1이라는 값을 넣은 다음 

jmp 구문을 따라 func+58 부분으로 이동을 하게 됩니다.

 

 

+58로 이동하게 되면 cmp 구문을 사용해서 rbp-0x14의 값과 9라는 값을 비교하고 있습니다.

하지만 우리가 방금 확인한 +11 에서 rbp-0x4에는 1이라는 값을 넣어주었으니 9보다 작은 값이 될 것 입니다.

 

jle 구문을 통해서 다시 +20의 주소로 이동하는것을 볼 수 있는데, jmp도 이동이고 jle도

이동인데  그러면 두개의 차이점이 무엇인지 알아야겠죠??

 

먼저 jmp는 조건없이 무조건 점프를 하는 명령어 입니다

그러면 jmp는 무조건 해당 주소로 이동을 하겠죠? 하지만 jle는 조금 다름니다.

jle는 비교 결과 값이 '0'이거나(ZF=1), 작을 경우 해당 주소로 점프하는 명령어 입니다.

그러니까 cmp구문으로 비교했을때 9보다 작을 경우 +20으로 이동하는 것 입니다.

 

 

이제 +20 부터 다시 살펴봅시다.

edx에 rbp-0x14의 값을 넣어주고 있습니다. 그리고 eax에 rbp-0x4의 값을 넣어줍니다.

하지만 +11부분에서 우리는 rbp-0x4에 1이라는 값을 넣었기 때문에 eax의 값은 1이 됩니다.

그리고 더해준 값을 ecx에 넣게됩니다.  그 다음에 printf 함수를 호출하고 실행을 시킨다음 

add를 통해서 1씩 증가 시켜주고 있습니다.  그러다가 rbp-0x4가 9와 같아지면

이 반복되는 코드를 빠져나오는데요 반복문을 사용한거 같습니다. 

 

 

위의 실행결과로 보아서 +20의 rdx가 우리가 입력한 값이 되고

rax가 반복하는 횟수 같습니다, +26만 봐도 rdx가 앞에오는데

실행결과에서도 우리가 입력한 3이라는 값이 앞에 오기때문에 

rdx의 값은 입력한 값이고 rax의 값은 반복하는 횟수라고

추측을 할 수 있습니다. +49의 부분에서 함수를 호출하고 난 다음,

add를 통해서 eax의 값을 1씩 증가 시키고 있습니다.

그렇다는 것은 eax의 값이 1씩 증가를 하고 9와 같아지면

반복을 멈춘다는 이야기가 됩니다.

 

 

반복을 멈춘뒤에는 바로 종료가 되는것을 확인 할 수 있습니다,

 

 

정리를 해보면 main 함수안에서 scanf 함수를 이용해 값을 입력하면

입력된 값은 func 함수의 인자가 되어서  edi에 저장이 되고, eax는 반복하는

횟수를 저장하게 됩니다. func이라는 함수의 내부에서 9번동안 덧셈을 반복하고

그 결과를 출력하고 난뒤 종료가 되는것을 알 수 있습니다.

여기까지 func 함수를 알아보고 homwork.c의 프로그램을 분석해보았습니다.

 

 

이제 gdb 사용법도 알았고 그러면 이제 문제를 풀어봅시다 

사실 숙제로 내줬음 ㅋㅋㅋㅋ

그럼 해당 파일을 gdb를 이용해서 풀어봅시다 

 

 

자 근데 여기서 일반 gdb랑 조금 다른거 같죠?

그렇습니다 gdb-peda라고 하는데 설치방법은 이러합니다

 

git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit

 

위와 같이 설치를 해주시고 gdb를 실행시키면 gdb-peda로 실행이 됩니다 

그럼 차이점이 무엇인가요??

PEDA는 Linux환경에서 동작하며 binary분석 및 Exploit을 도와주는 도구라고 보시면 됩니다.

-> peda 설명 https://go-madhat.github.io/gdb-peda/

그럼 문제의 homework.c를 보도록 합시다.

(이제 disass main은 당연히 자동으로 나와야겠죠?)

 

 

하나하나 살펴보아야 하지만 대충 확인을 해보면  +49에서 scanf함수를 이용하고 있습니다.

그러면 입력값이 들어가는것이 있다고 유추해볼 수 있습니다.

한줄씩 살펴볼까요? -> breakpoint를 걸어줍니다.

 

 

?????

우리가 봤었던 gdb화면과는 살짝 다르죠?? 

이게 바로 gdb-peda 입니다. 기존의 gdb는 레지스터 및 스택을 따로 봐야 했었지만

peda에서는 한번에 확인이 가능하죠  -> 정말 편리한 기능입니다. 

 

 

코드를 살펴보면 main함수를 확인하기 전에 위에 func함수를 볼 수 있는데

별도의 함수가 있는거 같습니다. 자 그러면 레지스터 정보를 확인 해볼까요??

 

 

프로그램을 실행했을때 아직은 큰 변화가 보이지는 않는것 같습니다.

그도 그럴게 맨처음 코드를 보면 스택프롤로그 즉 스택이 시작되는 부분이기 때문이죠

메인 함수 +8에 0x28의 값을 rax에 mov하고 있는데 해당 함수로 이동한 다음

값을 확인해 보도록 합시다.

 

 

rax값이 변경이 된것을 볼 수 있습니다. 하지만 이상태로는 식별이 힘드니

아래로 계속 실행하면서 분석을 하도록 하겠습니다.

 

 

xor eax eax를 통해서 rax 값을 0으로 초기화를 시켜주는것을 볼 수 있습니다.

eax에서 확장된 부분이 rax이니까 당연히 레지스터에서는 rax가 0이 되겠죠??

계속 실행을 시켜보겠습니다.

추측을 해보면 아까 peda 이전의 gdb에서 scanf함수를 확인을 했기때문에 입력을 하고 나면

rax값이 입력값으로 바뀔것이라고 생각할 수 있습니다.

 

 

다시 한줄을 실행시키니 rax값이 변경되었습니다. 

+30에 코드 때문이겠죠? 그러면 지금 rax값은 rbp-0xc의 값이 lea되었다라고 보면 됩니다.

그리고 다시 rax값을 rsi로 mov를 해주고 있습니다.  (rbp-0xc의 값이 rsi로 들어가고 있습니다.)

여기서 코드를 보면 다시 +44부분에서 다시 eax에 0을 넣어주고 scanf함수를 호출하네요 

 

 

scanf함수를 실행을 하고나서 조금더 실행을 시켜보면 

func 함수를 실행하고 있는것을 볼 수 있습니다. 

 

 

코드를 한줄 더 실행해보면 scanf 함수를 호출해서 입력을 받는것을 확인 할 수가 있습니다.
그래서 3을 입력을 해주면??  rax에 3이 들어가지 않을까요?

 

 

여기서 코드를 살펴보면 rbp-0xc의 값을 eax로 mov를 해주고 있네요 

그러면 이 함수를 실행하면 rax에 3이라는 값이 저장이 될것 같아 보입니다.

왜냐하면 3을 입력을 했고 그럼 3이라는 값이 rax로 들어가야하는데

코드를 보면 해당 주소의 값이 eax로 들어가서 입니다.

자 그럼 확인을 해볼까요?

 

 

역시나 rax에 우리가 입력한 3이라는 값이 들어가 있습니다.

그리고 다음 코드는 func라는 함수를 호출하고 있네요

한번 실행을 시켜보도록 하겠습니다.

 

 

실행결과가 나오고 있네요 그러면 func라는 함수는 실행결과로 보아

계속해서 덧셈을 반복해주고 있는거 같아 보입니다. (func라는 함수도 한번 봐야할것 같습니다.)

일단 main함수를 계속해서 보겠습니다.

 

 

+64부분에서 보면 eax값에 0을 넣어줌으로 이전에 실행했던 func함수를 실행하고

다시 원래대로 돌아가는 것을 볼 수 있는데요 아마도 함수를 실행하고

다시 원래의 상태로 돌려준 후 종료가 되는것 같습니다. -> 스택 에필로그 같아 보입니다.

 

 

생각대로 je 구문을 통해서 +89가 있는곳으로 점프(이동) 하게 되는데, 그곳에는 leave 명령어가 있습니다.

leave는 현재까지 썻던 메모리 스택을 깔끔히 비우고, 자신을 호출했던

메모리의 베이스 주소를 ebp에 다시 채우는 명령어 입니다.

-> 즉, 이제 까지 사용했던 자원들을 반납을 하게 되는것이죠 

정상적으로 종료되는 단계라고 볼 수 있습니다.

당연히 그다음 구문은 ret가 있어서 종료가 되는것을 확인 할 수 있습니다. (스택 에필로그)

 

여기까지 main 함수를 한번 gdb-peda를 사용해서 분석해보았습니다.

다음에는 func 함수를 한번 보도록 하겠습니다.

 

확실히 gdb-peda가 사용하는 측면에 있어서 편하긴 하네요 

그럼 20000~

저번에는 GCC와 GDB를 설치해서 메인 함수를 보는 과정을 보았는데요

이번에는 하나하나 분석을 해보도록 하겠습니다.

 

 

1차시에 봤던 GDB를 이용하여 main 함수를 보았는데요 

앞에는 주소값이 나와있고, <+숫자>는 상대적인 위치를 의미 합니다.

그 옆에는 어셈블리 명령어가 나와있습니다.

 

하나하나 분석을 하기 위해서는 Break point를 설정을 해야 합니다 

한줄 한줄 분석을 해야하기 때문이죠 

 

그럼 break point는 어떻게 하는것이냐 -> 이거는 구글링 하세요 

 

(gdb) b *main

 

이렇게 berak point 말 그대로 프로그램이 실행되다가 멈추는 지점을 설정 해주었습니다.

((b * 주소값) -> 이렇게 하면 해당 주소에 break) 또는 (b *상대적 위치)

당연히 첫번째 줄 부터 확인을 해야하니까 main 함수의 시작부분을 break 해주어야 되겠죠?

 

 

그러면 이렇게 break가 걸린것을 볼 수 있습니다.

아래의 명령어로 입력을해도 확인 할 수 있습니다.

 

(gdb) info b

 

 

똑같은 결과가 나온것을 확인 할 수 있습니다.

info b를 해보면 맨앞에 숫자가 보이는데 이것은 break의 번호 입니다.

(0x0000000000006d7은 해당 함수의 주소값이 됩니다.)

 

우리가 첫번째로 break를 걸어두었으니 1번이 되는겁니다.

아래 명령어로 break를 해제 할 수도 있습니다. 

(번호를 입력하지 않는다면 모든 break를 해제합니다.)

 

(gdb) info b
(gdb) delete "Number"

 

그럼 이제 주소값이랑 break는 알겠는데... 

뒤에 숫자는 뭐죠??? (맨위에 GDB 확인을 하고 오시길)

 

숫자는 말그대로 eip로부터 상대적 위치를 의미합니다.

그러니까 main함수는 eip로 부터 상대적 위치가 0이라는것을 의미합니다

시작 부분이니까요

그럼 모든 break를 풀고 프로그램을 실행시켜 보겠습니다.

 

(gdb) run

 

 

우리가 코드를 입력했던데로 결과가 제대로 나오는것을 볼 수 있습니다.

 

 

위 처럼 break를 main에 걸고나서 실행을 시키니 바로 main의 시작

부분에서 멈추는것을 볼 수 있습니다.

그러면 해당 함수에 무슨 값이 들어있는지 확인을 해보겠습니다.

 

x/t "메모리 주소" 2진수로 확인하기 
x/o "메모리 주소" 8진수로 확인하기 
x/d "메모리 주소" 10진수로 확인하기 
x/u "메모리 주소" 부호없는 10진수로 확인하기 
x/x "메모리 주소" 16진수로 확인하기 
x/c "메모리 주소" char로 확인하기
x/f "메모리 주소" 부동소수점으로 확인하기
x/s "메모리 주소" 스트링으로 확인하기

info reg 레지스터 확인
info reg "레지스터" 특정 레지스터 확인
info break 브레이크 포인트 확인

run "args" 처음부터 실행하기
continue 멈춘 부분부터 계속 실행하기
ni 한 스탭 실행 후 멈추기

x/bx $rsp 1바이트씩 확인하기
x/hx $rsp 2바이트씩 확인하기
x/dx $rsp 4바이트씩 확인하기
x/gx $rsp 8바이트씩 확인하기

 

위의 명령어를 참고하여 분석을 진행하겠습니다.

 

 

break를 걸어주면 화살표 모양이 생기는데

지금 이 위치에 Break가 걸려있다는 의미가 됩니다

여기서 레지스터의 정보를 확인하여 어떤값이 들어가 있는지 보면 

 

 

현재 rsp 와 rbp , rip에만 동일한 값이 들어가있는 것을 확인 할 수 있습니다.

rip는 다음에 실행할 명령어를 가리키는데 지금 현재 main 함수 즉, break를 걸어놓은 곳 입니다.

그렇다면 rsp는 지금현재 0x7ffffffee2eB라는 값이 들어가있는데 스택의 사용자 영역에

들어와 있다는 것을 의미합니다.

ni 명령어로 실행한다음 disass main으로 main 함수들을 확인해보면 

화살표가 한칸 아래를 가리키고 있습니다. 그럼 이상태에서 rsp 값을 확인해보면 되겠죠?

 

 

아무것도 현재 push를 하고 있지 않기 때문에 rsp값은 변경되지는 않은것 같네요

 

 

하나하나 분석을 진행하는데 우리가 코드에 작성해두었던 문자열을 확인하기 위해서 

한줄 한줄 문자열로 확인을 해보았는데 shj라는 문자열이 +23의 위치에 확인이 되고 있었습니다.

대부분 스택은 4바이트 씩 쌓이니까 +27의 위치에 혹시나 하고 확인을 해봤는데 

역시나 shj라는 문자열이 들어가있습니다.

 

 

계속 보던 도중에 다음과 같은 명령어가 확인이 되었는데요

딱봐도 0x14 즉 20이라는 값을 rbp-0x18이라는 공간에 mov하고 있는것을 알 수 있습니다.

그래서 여기가 age라는 함수 일 것이라고 예상을 했습니다.

그리고 다시 edx로 mov를 하고 있습니다. 

 

 

그래서 +44의 위치에 info reg를 이용해서 레지스터의 값을 확인해보니까

rdx에 정상적으로 20이 들어가있네요 

 

 

그러면 여기서 아까 edx에 mov를 했는데 rdx에 들어가는 이유가 무엇이냐

64비트 체제이기 때문이라고 할 수 있는데요 

rdx는 기존의 32비트 체제에서 사용하던 edx의 확장판이기 때문입니다.

그러므로 굳이 8바이트를 다 쓰지 않고 4바이트만 사용하는

경우에는 rdx의 절반인 edx를 사용하고 있는 것이죠 (다른 레지스터도 똑같습니다.)

그래서 아까 shj라는 문자열을 +23의 위치에 확인하고 나서

+27에 들어가 있는 이유도 같은 이유 입니다. (4바이트씩 쌓였기 때문)

 

 

여기서 a라는 함수를 호출하고 종료하는것 같은데요 한번 확인해보겠습니다.

 

 

a라는 함수가 실행이 되고 다음으로 넘어가게 됩니다.

그러면 그 뒤 부분은 스택을 정리하고 정상적으로 프로그램이 종료가 되겠네요 

 

 

이렇게 스택을 정리하고 종료가 됩니다.

그래서 요약을 하자면 

이 프로그램은 name의 shj라는 문자열과 int age = 20 이라는 값을

함수 a로 call해서 실행한후 종료되는 프로그램 입니다.

이렇게 GDB를 이용해서 프로그램을 분석을 해보았습니다.

 

그럼 20000

일단 시작하기 앞서서 GCC와 GDB 설치가 필요하겠죠?

 

apt install GDB
apt install GCC

 

해당 코드를 작성을 해두었습니다

 

#include <stdio.h>

void a(char * name, int age){
	
	printf("%s %d\n",name,age);

}

int main(void){
	
	char name[10] = "shj";
	int age = 20;
	
	a(name, age);
	
	return 0;
}

 

이제 컴파일을 해야하는데 Gcc를 이용해서 컴파일을 하도록 하겠습니다.

 

sudo apt-get install GCC

 

 

정상적으로 설치가 된것을 확인 할 수 있습니다

여기서 fatal error가 나는 것이 정상입니다 아직 아무런 파일도 넣지 않았기 때문이죠

그래서 우리가 미리 만들어 두었던 코드를 컴파일을 해보겠습니다.

 

gcc cdecl.c

 

 

정상적으로 컴파일이 완료가 되면 a.out이라는 파일이 생성이 됩니다.

프로그램 이름 앞에 디렉토리 위치 정보인 ./를 추가해야

현재 디렉토리에 있는 명령어가 실행되는데, 여기서 마침표(.)는 현재 디렉토리를 의미하고,

슬래시(/)는 디렉토리를 구분하는 문자가 됩니다.

 

gdb a.out
disass main

 

gdb를 실행시키고 나서 해당 프로그램의 메인함수를 보기 위해서 disass main을 하였습니다.

그러면 아래와 같이 나오는데요 하나하나 살펴보겠습니다.

 

 

여기서 조금더 편하게 보기위해서 인텔문법으로 변경을 해주었습니다.

 

set disassembly-flavor intel

 

 

변경이 된 것을 확인 할 수 있습니다.

이번에는 gdb 설치와 실행을 해보았는데요 

이어서 다음 포스팅에는 분석을 진행하도록 하겠습니다

그럼 20000

+ Recent posts