관리 메뉴

개발자비행일지

GDB사용법, 리버싱 기초(리눅스 elf파일) 본문

▶ 모의해킹 공부

GDB사용법, 리버싱 기초(리눅스 elf파일)

Cyber0946 2020. 4. 6. 17:05

GDB 사용법

기본조작법

파일을 gdb로 불러와서 디버거를 실행하는 법

è  gdb <파일이름>

디버거에서 파일 로드

è  file <파일이름>

실행중인 프로세스 디버깅

è  gdb <프로세스명> <pid>

디버거에서 Attach

è  attach <pid>

è  프로그램의 시작부터 하는게 아니라, 현재 실행중인 시점에서부터 분석하고자 할 때, 사용한다.

소스코드와 함께 보기

è  gdb -tui

실행시 기본조작법

프로그램을 처음부터 실행

è  r

è  인자가 있을 경우는 r 인자1 인자2

디스어셈블 코드 보가

è  disas <위치 또는 함수명>

소스코드 보기

è  gcc -g명령으로 파일을 만들고 gdb에서 불러와서 l명령을 주면 소스코드 볼 수 있음

프로그램 실행 계속

è  c

현재 함수가 종료될 때까지 실행

è  f

다음 명령어 실행

è  ni

브레이크 포인터 설정

è  b

브레이크포인트 정보 확인

è  I b

인텔 방식으로 변경

è  set disassembly-flavor it

정보를 조회하는 방법

레지스터 정보를 조회

è  info reg

전체 지역번수(현 위치) 조회

è  info locals

전체 전역변수

è  info variables

출력 형식

è  x/8x <확인대상> 16진수 형식으로 8바이트 보기(x/4wx)

è  x/I <확인대상> 대상 주소의 어셈블리 명령어 확인

è  x/s <확인대상> 대상 주소의 문자열 확인

gdb에서 layout asm 또는 layout regi 등으로 현재의 상황을 확인할 수 있음

GDB를 사용해서 실제로 elf 파일을 리버싱 해보자.

첫번째 분석

Crackme1

프로그램 기본 동작 ./crackme_1 으로 실행시켜, 봤을 때는 사용법이 출력되도록 되어 있다.

è  실행을 시키고 비밀번호를 매개변수로 입력시켜줘야 함을 알 수 있다.

file crackme_1 명령으로 crackme_1  파일에 대해서 정보를 얻는다.

è   ELF 32비트 형식의 실행 파일이며, strip기능을 사용하지 않은 elf 파일임을 알 수 있다.

파일에 있는 string 데이터를 strings <파일명>로 확인한다.

!!! 보자마자, WhoAmI, Cracked!!!!! 가 출력되는 것을 확인할 수 있다. 하지만 이걸 못찾았다고 가정하고 GDB를 사용한 리버싱으로 이 문제를 해결해 보자.

우리의 예측이 맞는지는 아래처럼 실제로 넣어보면 Password가 통과됨을 알 수 있다.

 

gdb를 사용하기 전에 nm을 사용해서 이 프로그램이 동적으로 가져오는 정보, 라이브러리를 확인해 보자. 아래의 정보를 보면, printf puts, strcmp 가 온다는 것을 알 수 있다. 여기서 nm -D옵션은 동적으로 연결되는 정보들을 출력해준다.

, 이제 본격적으로 GDB를 사용해 보자.

layout asm, layout reg을 사용하면, gdb를 좀더 시각 적으로 사용할 수 있다.  

 

gdbcrackme1 Main을 살펴보면 다음과 같다. 우리가 눈 여겨 봐야 할 부분은 아래의 4줄이다.

(gdb) disass main

Dump of assembler code for function main:

   0x0804847d <+0>:     push   %ebp

   0x0804847e <+1>:     mov    %esp,%ebp

   0x08048480 <+3>:     and    $0xfffffff0,%esp

   0x08048483 <+6>:     sub    $0x10,%esp

   0x08048486 <+9>:     cmpl   $0x2,0x8(%ebp)

   0x0804848a <+13>:    je     0x80484a8 <main+43>

   0x0804848c <+15>:    mov    0xc(%ebp),%eax

   0x0804848f <+18>:    mov    (%eax),%eax

   0x08048491 <+20>:    mov    %eax,0x4(%esp)

   0x08048495 <+24>:    movl   $0x8048570,(%esp)

   0x0804849c <+31>:    call   0x8048340 <printf@plt>

   0x080484a1 <+36>:    mov    $0x0,%eax

   0x080484a6 <+41>:    jmp    0x80484d5 <main+88>

   0x080484a8 <+43>:    mov    0xc(%ebp),%eax

   0x080484ab <+46>:    add    $0x4,%eax

   0x080484ae <+49>:    mov    (%eax),%eax

   0x080484b0 <+51>:    movl   $0x8048585,0x4(%esp)

   0x080484b8 <+59>:    mov    %eax,(%esp)

   0x080484bb <+62>:    call   0x8048330 <strcmp@plt>

   0x080484c0 <+67>:    test   %eax,%eax

   0x080484c2 <+69>:    jne    0x80484d0 <main+83>

   0x080484c4 <+71>:    movl   $0x804858c,(%esp)

   0x080484cb <+78>:    call   0x8048350 <puts@plt>

   0x080484d0 <+83>:    mov    $0x0,%eax

   0x080484d5 <+88>:    leave 

   0x080484d6 <+89>:    ret

먼저, 이 프로그램은 실행 된 다음에 ebp+0xc 지점과 0x2를 비교한다. 이것은 왠지 인자의 개수를 확인하는 부분처럼 느껴진다. 확인해보자.

프로그램에 인자를 넣고 실행시키고 분석하기 위해서 main+43 부분에 중단점을 b 명령어로 설정해주고, r 명령어와 그 뒤에 AAAAAAAA을 인자로 주어서 실행해 보자.

ebp+0xc 지점의 주소에 들어있는 값은 0xbffff094라는 주소이다. 그럼 이 주소에 어떤 문자열이 들어 있는지 x/s *문자열 주소로 확인해 보자!

è  파일의 경로가 들어 있는 것을 알 수 있다.

그럼 그 다음에는 어떤 값이 들어 있을까 ??? 이걸 확인해 보면 다음과 같다. 우리가 실행할 때 넣어준 인자가 들어 있다.

자 이제 그 다음으로 strcmp를 호출하기 전에 어떤 값들이 인자로 들어가고 어떤 비교가 일어나는지 확인해 보자. 어셈블리 코드는 다음과 같다.

0x080484b0 <+51>:       mov    DWORD PTR [esp+0x4],0x8048585

0x080484b8 <+59>:       mov    DWORD PTR [esp],eax

0x080484bb <+62>:       call   0x8048330 <strcmp@plt>

다시 b *main+62 명령으로 새로운 중단점을 설정하고

c 명령으로 실행시킨다.

strcmp를 실행하기전에 register 정보는 다음과 같다. 그럼 esp를 확인해 보자.

esp의 최상단에 있는 oxbffff2bc0x08048585에는 어떤 값들이 있을 까? 확인해 보자.

이걸 통해서 , AAAAAAAA 우리가 입력한 매개변수와, WhoAmI 라는 문자열을 비교한다는 것을 알 수 있다.

자 그럼 set 명령어로 0xbfffefe의 정보를 0x08048585로 바꿔주어 보자.

그 다음 c로 실행시켜보면 다음과 같다!!!! 통과했다.

이런 과정을 통해서 우리는 elf 파일을 gdb를 써서 리버스 엔지니어링 할 수 있다.

우리가 리버싱 한 파일의 소스코드는 다음과 같다.