본문으로 바로가기

어셈블리 와 C언어, 리눅스의 이해

category Pwnable 2018. 7. 4. 11:15

1. objdump 를 이용해 elf 파일 내부를 자세히 보자.


objdump -x 옵션을 모든 이용가능한 헤더 정보를 준다.

아래 그림을 보고 이해해보자.



굉장히 복잡해 보이지만 차근 차근 하나씩 알아보도록 하자.


hello2:     file format elf32-i386         //파일 형식이 elf라는것을 알 수 있다.

architecture: i386, flags 0x00000112      //하드웨어 타입을 알 수 있다.

start address 0x08048080               //메모리 상의 주소( 여기부터 프로세스가 올라간다.)


LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12(4kb) // off(파일 오프셋), vaddr(가상 메모리)

       filesz 0x00000096 memsz 0x00000096 flags r-x

  0x96은 150byte이지만 실제 4kb가 할당된다. flag - 읽고 실행가능 (권한을 보고 이 부분이 text segment임을 알 수 있다.)


LOAD off    0x00000098 vaddr 0x08049098 paddr 0x08049098 align 2**12(4kb) // 4kb 지나서 다음 segment 존재

       filesz 0x00000011 memsz 0x00000011 flags rw-

  0x11은 17 byte이지만 실제 4kb가 할당된다. flag - 읽고 쓰기 가능 (권한을 보고 이 부분이 data segmet임을 알 수 있다.)



Sections:

Idx Name          Size         VMA       LMA       File off    Algn

  0 .text         00000016  08048080  08048080  00000080  2**4        // size 는 코드의 전체 크기 이다.(22byte)

                  CONTENTS, ALLOC, LOAD, READONLY, CODE            // vma = entry point 실제 시작 위치 주소


  1 .data         00000011  08049098  08049098  00000098  2**2       // data segment(data, bss, heap 영역)

                  CONTENTS, ALLOC, LOAD, DATA                        // data 초기화된 데이터, bss 초기화 되지 않은 데이터


  2 .bss          00000003  080490a9  080490a9  000000a9  2**0

                  CONTENTS


  3 .comment      0000001f  00000000  00000000  000000ac  2**0


2. red hat 6.2


최근에 나온 ubuntu, centos 등 여러 OS는 최신의 메모리 보호 기법이 적용되어 있다.

하지만 처음 시작하는 입장에서 이러한 메모리 보호 기법이 적용되어 있으면 공부하기 너무 어렵다.

따라서 메모리 보호기법이 적용되어있지 않은 red hat 6.2를 사용한다.


주로 0x00000000 ~ 0xbfffffff 공간은 프로세스가 사용하고,  0xc000000~0xffffffff 공간은 커널이 사용한다.


hello2 elf 파일을 대상으로 분석해보자.


start address는 0x08048080 -- 이 주소가 파일이 로딩되는 시작 위치이다.


1번에서 objdump -x 옵션을 통해 헤더 정보를 확인했지만, 이번에는 objdump -d 옵션을 통해 disassemble을 해보자.



또한 xxd 명령을 통해 hexadump를 할 수 있다. 우선 지금 단계에서는 이를 통해 elf 파일임을 확인 가능하다.




3. 어셈블리와 C언어


1) 데이터 타입   

assembly와 C 모두 숫자(정수, 실수), 문자('), 문자열(")을 데이터 타입으로 갖는다.


2) 레이블 vs 변수

assembly는 레이블, C언어는 변수라는 개념을 사용한다.


레이블은 이름표라고 생각하는 주소이다.

변수는 값, 주소, 크기를 모두 갖는다.


3) 데이터 크기 (assembly)


a. 접두사 : d(data), res(bss)


b. 단위:


byte        unit            c

1           (b)byte        char

2          (w)word       short

4           (d)dword       int, float, pointer

8           (q)qword      long long, double

10         (t)tenbyte       x


4) 어셈블리는 반드시 segment 분류가 필요하다.


C언어

int apple = 10;

int orange;


nasm

segment .data

apple    dd    10


segment .bss

orange    resd    1


4. 어셈블리 프로그래밍 및 이해


1) C라이브러리를 사용하지 않고 프로그래밍 하기



segment .data

string  db      'hello, world!!!', 10(new line), 00(문자열의 끝에 항상 null 문자가 들어가야햔다.)

- "hello, world!!!\"null 로 생각하면 쉽다.

- 'hello, world!!!'의 주소가 string(레이블)에 들어가 있다고 생각하면 된다.

- 레이블의 이름은 중복될 수 없다.

- db는 nasm에서 사용하는 데이터 타입니다.

- 현제 db는 총 17바이트이다.

- C언어로 비유를 들면 char stirng[] = 'hello, world!!!\nNULL'


segment .bss

buffer resb     1024                    // char buffer[1024]; 초기화 되지 않았다.


segment .text

global _start


_start:                                 // entry point(gcc는 main, gcc 이외는 _start로 시작한다.)

        mov     eax,    4

        mov     ebx,    1

        mov     ecx,    string

        mov     edx,    16

        int     0x80


출력값으로 string에 저장된 문자열이 그대로 나오는것을 확인할 수 있다.

하지만 system call 관련 이야기를 해야하므로 추후에 더 얘기하기로 하고 프로그래밍 관점에서 보자.


2) C 라이브러리를 사용한 어셈블리 프로그래밍


C 라이브러리를 사용할 때 반드시 extern 키워드를 사용해야한다.

일반적으로 push의 개수는 함수의 인자 개수와 같다.

hello3를 보면 apple의 개수가 10개로 예상을 했는데 134517871으로 알수 없는 값이 나왔다.
무슨일이 발생한걸까?

이유는 간단하다. apple은 레이블 그 자체로 주소를 담고 있다.
즉 우리는 10이 저장되어 있는 apple 레이블의 주소를 출력한 것이다.

주소가 아닌 값에 접근하기 위해 아래와 같이 코드를 약간 수정하고 다시 컴파일 해보자.



dword []를 통해 주소에 있는 값에 접근 할 수 있다. C언어에서의 포인터 개념과 유사하다



5. 연습문제


C라이브러리를 이용하여

(0)_(0)(0)_(0)

(=^.^=)(*^.^*)

(_m_m_)(_m_m_) 를 어셈블리로 짜라



초기화 되지 않고 사용되는 데이터가 없어서 .bss 세그먼트를 이용하지 않고 프로그래밍 하였다.