본문 바로가기

Learning/└◆Reversing

01_시스템해킹 리버싱

FTZ를 리버싱의 주제로 선정한 이유는 FTZ에 각 LEVEL 에서 사용하는 소스의 목적을 분석하고

그에 맞는 의사 코드를 gdb를 이용해 디버깅하여 리버싱을 통해 복원해보는 과정을 주로 다루며

원본 코드 소스와 비교 해보는 과정을 학습하기 위함이다.

최종 목적은 level15이후의 버퍼 오버 플로우를 공부하고 그 단원의 코드를 분석해보는 것에 있다.

 

참고자료 

00_실습 환경 구축.hwp

01_시스템해킹_리버싱_2014_1129_1.hwp


Reverse Engineering on HackMe System


학습목표

■ 리버싱을 통한 의사 코드(가상 코드) 생성

■ exploit 코드 개발

상대편의 취약성을 공격하는 코드



HackMe(ftz.hackerschool.org)


참고 사이트 :

http//inhack.org/wordpress/?cat=68    문제 풀이에 대한 참고 사이트

http://www.codeengn.com/    (Google Search : site:codeengn.com intext:index.of.)


■ 학습 목적

  • 메모리 구조에 대한 이해
  • SetUID/SetGID/Sticky Bit 이해
  • gdb 기본 사용법

(1) 운영체제 기본 정보 확인

[level1@ftz level1]$ cd tmp

[level1@ftz tmp]$ cat /etc/redhat-release

Red Hat Linux release 9 (Shrike)


[level1@ftz tmp]$ uname -a

Linux ftz.hackerschool.org 2.4.20-8smp #1 SMP Thu Mar 13 16:43:01 EST 2003 i686 athlon i386 GNU/Linux


[참고] Redhat 계열의 운영체제 버전 발전 과정

RedHat 6.X -> RedHat 7.X -> RedHat 8.X -> RedHat 9.X

-> RHEL 2.X -> RHEL 3.X -> RHEL 4.X -> RHEL 5.X -> RHEL 6.X -> RHEL 7.X

 

[참고] 리눅스 커널 버전에 대해서

Kernel 2.2.X -> Kernel 2.4.X -> Kernel 2.6.X


■ 메모리 레이아웃(Layout)에 대한 예제

#include <stdio.h> 

#include <stdlib.h>


int retVal = 0;

int outVal;


int main(void)

{


char string[] = "hello"; 

char *ptr;

static int output = 1;


ptr = (char *)malloc(sizeof(string));

                         

printf("%s\n", string);


return retVal;


의미없는 구문이다.



$ cd tmp

$ vi hello.c


$ gcc -o hello hello.c

$ ./hello

hello

$


■ Terms

CPU <------> MEM <------> DISK


■ 메모리 구조


세그먼트 메모리를 나눠놓은 것
프로그램이 실행되면 낮은 address 부터 차근차근 올라간다.
  • Text
    함수의 Code값이 위치하는 부분

  • Data
    전역 변수가 위치한 Data 영역과 정적 변수가 위치한 BSS 영역으로 나뉜다.

  • Heap
    저장공간으로 사용, malloc이라는 함수로 메모리 공간을 확보 했을 때 할당된다.
    동적할당을 할 때 사용, 메모리의 효율성이 가장 좋다.

  • Stack
    먼저 들어간게 나중에 나오는 거꾸로 된 구조
    일반적으로 지역함수, 함수의 인자값, 복귀 조수 등이 위치, CPU에서 접근 속도가 가장 빠름.


■ 인자가 전달되는 방식의 예제

$ vi structure.c

#include<stdio.h>


void function(int a, int b, int c)

{

char buffer1[5];

char buffer2[10];

}


int main()

{

function(1,2,3);


return 0;

}


$ gcc -o structure structure.c

$ ./structure

$ echo $?

0
정상적으로 동작한다.(main함수의 return값)


■ gdb를 통해 structure 프로그램을 disassembly 해보기


(용어) 어셈블리어(Assembly)언어

■ 기계어 : CPU의 명령어(전기신호)를 숫자로 표현한 것

-> CPU의 명령어는 CPU제조회사에서 일정한 규칙을 두고 정한다.

■ 어셈블리 언어: CPU와 직접 대화 할 수 있는 언어(명령어의 집합)


disassembly ?

커널이 인식할수 있는 바이너리 형태로 만드는 과정 -> 컴파일

바이너리 형태를 C언어 형태로 만드는 과정 -> 디컴파일


어셈블리 언어란

1과 0으로 되어있는 코드는 분석,수정이 힘들다.

언어형태를 취할수 있게 변환하는 과정

바이너리 코드와 동일한 포맷

$0xfffffff0      ---->어셈블리      ---> 보기좋게 출력

보기좋은 출력값  ---->디스어셈블리 ---> $0xfffffff0


해석하기 어려운 코드를 어셈블리언어 형태에서 다시 c형태로 변환



$ gdb structure (gdb : GNU debugger)

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)

Copyright 2003 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu"...

(gdb) disassemble main

Dump of assembler code for function main:

0x080482fc <main+0>:    push   %ebp

0x080482fd <main+1>:    mov    %esp,%ebp

0x080482ff  <main+3>:    sub    $0x8,%esp

0x08048302 <main+6>:    and    $0xfffffff0,%esp

0x08048305 <main+9>:    mov    $0x0,%eax

0x0804830a <main+14>:   sub    %eax,%esp

0x0804830c <main+16>:   sub    $0x4,%esp

0x0804830f <main+19> :   push   $0x3       function(1,2,3); = $0x3,2,1 이다.

0x08048311 <main+21>:   push   $0x2

0x08048313 <main+23>:   push   $0x1

0x08048315 <main+25>:   call   0x80482f4 <function> 순서대로 함수호출, 변수의 선언 및 초기화하는 작업 수행

0x0804831a <main+30>:   add    $0x10,%esp         함수호출시 순서대로 나오기 때문에 거꾸로 들어가야한다.

0x0804831d <main+33>:   mov    $0x0,%eax

0x08048322 <main+38>:   leave

0x08048323 <main+39>:   ret

End of assembler dump.

(gdb)

  • 인자값을 a=3 할당하고, b=2 할당하고, a=1 할당한다.

  • 스택(Stack)은 지역변수가 쌓일수록 메모리의 낮은 주소 방향으로 데이터가 쌓이는 특이한 구조이다.

[참고] 자세한 분석 과정에서 대해서는 다음 문서를 참고
structure 프로그램 실행시 자세한 디버깅 과정

structure.c 파일의자세한분석과정.hwp



위의 내용에 대한 해석

 

1 (gdb) disassemble main

main 함수를 disassemble(binary -> assembley) 한다.

 

2 Dump of assembler code for function main:

main 함수의 assembler가 덤프를 시작한다.

 

3 0x080482fc <main+0>: push %ebp

EBP 내용을 stack 주소를 넣는다.(최초의 프레임 포인터(ebp) 값을 스택에 저장한다.)

ebp 바로 전에 ret(return address)가 저장된다.

ebp(extended base pointer)는 함수 시작 전의 기준점이 된다.

스택에 저장된 ebpSFP(Saved Frame Pointer)라고 부른다.

ret(return address)에는 함수 종료시 점프할 주소값 저장


4 0x080482fd <main+1>: mov %esp,%ebp

현재 esp 값을 ebp 레지스터에 저장한다.

"push %ebp", "mov %esp %ebp"는 새로운 함수를 시작할 때 항상 똑같이 수행하는 명령으로 프롤로그(Prologue)라고 부른다.

 

5 0x080482ff <main+3>: sub $0x8,%esp

esp 레지스터 가리키는 주소에서 0x8만큼 주소를 뺀다.

일반적으로 변수를 설정할수 있는 공간을 확보 하는것이다.

변수의 종류(: char or int)와 개수에 따라서 빼는 값이 틀려진다.