1. C프로그래밍
- visual studio (컴파일러 아님), 이클립스
- IDE : 통합 개발 활경
- IDE는 compiler + 편집기 + 디버거를 뜻한다.
리눅스에서의 프로그래밍
- IDE를 따로 사용하지 않고 compiler, editor, debugger를 사용한다.
- compiler : GCC
- editor : VI
- debugger : GDB
hello.c 를 작성해보자.
[root@M4RC0 /root]# vi hello.c
아래와 같이 gcc로 컴파일을 할 수 있다.
단, gcc 컴파일 시 옵션을 주지 않아 새로운 이름을 설정해주지 않으면 기본 파일명은 a.out으로 나온다.
2. 컴파일 과정
gcc -v 소스파일.c : 소스파일 컴파일 과정을 보여준다.
[root@M4RC0 /root]# gcc -v hello.c
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs
gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/cpp -lang-c -v -undef -D__GNUC__=2 -D__GNUC_MINOR__=91 -D__ELF__ -Dunix -Di386 -D__i386__ -Dlinux -D__ELF__ -D__unix__ -D__i386__ -D__i386__ -D__linux__ -D__unix -D__i386 -D__linux -Asystem(posix) -Asystem(unix) -Acpu(i386) -Amachine(i386) -Di386 -D__i386 -D__i386__ -D__tune_i386__ hello.c /tmp/cc2jKsys.i
GNU CPP version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) (i386 Linux/ELF)
#include "..." search starts here:
#include <...> search starts here:
/usr/i386-redhat-linux/include
/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include
/usr/include
End of search list.
/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/cc1 /tmp/cc2jKsys.i -quiet -dumpbase hello.c -version -o /tmp/ccAdZBoJ.s
GNU C version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) (i386-redhat-linux) compiled by GNU C version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release).
as -V -Qy -o /tmp/ccI9ZPW0.o /tmp/ccAdZBoJ.s/t
GNU assembler version 2.9.5 (i386-redhat-linux) using BFD version 2.9.5.0.22
/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/collect2 -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtbegin.o -L/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66 -L/usr/i386-redhat-linux/lib /tmp/ccI9ZPW0.o -lgcc -lc -lgcc /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtend.o /usr/lib/crtn.o
실제로 보여지는 소스파일.c의 컴파일 과정이다.
전체 컴파일 과정에 대해 알아보자. (gcc --save-temps 소스코드.c -> Do not delete intermediate files)
1) 전처리 단계 - cpp( C PrerProcess)
- hello.c -> hello.i
- 메크로
- 헤더파일 처리
2) 컴파일 단계 -cc1
- hello.i -> hello.s
- 어셈블리 언어로 변환한다.
어셈블리 언어로 변환하는 이유는 바로 기계어로 변환할 수 없기 때문에 기계어로 변환 가능한 어셈블리어로 변환한다.
- 왜 어셈블리어를 사용하는가?
처음 0과 1을 문자에 일대일 대응한 문자이기 때문이다.
ex) 001010 -> push
011101 -> pop
- 바이너리(실행파일)은 0, 1로 이루어진 파일이다. 사람이 바이너리 파일을 직접 만드는것은 어렵기 때문에 소스를 작성해 어셈블리어로 변환 후 컴파일러에 의해 자동변환을 하는 것이다.
* visual studio에서 컴파일 에러가 발생하면 이 단계(컴파일)에서 에러가 발생한 것이다.
3) 어셈블러 - as
- hello.s -> hello.o
- 어셈블리어 -> 기계어
- 목적 파일을 생성한다(바이너리)
- object 파일로만은 라이브러리 파일에 있는 함수를 사용할 수 없다.
4) 링크(일반적으로 dynamic-linker 사용) - collect2, ld, gcc
- hello.o -> hello
- 목적 파일을 가지고 여러가지 라이브러리를 합쳐 하나의 바이너리를 만든다.
hello1.c를 작성해보자.
hello1.c를 가지고 컴파일 과정을 좀 더 자세히 확인해보자.
1) 전처리 단계
[root@M4RC0 /root]# cat hello1.i
매크로 함수가 처리된것을 알 수 있다.
2) 컴파일 단계
[root@M4RC0 /root]# cat hello1.s
C언어가 어셈블리어로 변환된 것을 볼 수 있다.
리눅스에서 바이너리 파일을 ELF 라고 부른다. ELF 임을 확인하는 방법은 PE의 헤더를 보면 알 수 있다.
3. 어셈블리어로 작성하기
어셈블리어로 작성하면 1), 2) 단계가 사라지고 3), 4) 단계만 이용한다.
c언어 -> 어셈 -> 기계어
x <-
위의 hello.c를 어셈블리어로 작성해보자.
(1). C 라이브러리를 이용한 어셈블리 프로그램 작성
3)번 과정 - nasm -f elf -o hello.o hello.asm
4)번 과정 - gcc -o hello hello.o
Segmentation fault 는 추후에 설명할 예정이니 잠시만 그냥 넘어가자.
hello.asm을 분석해보면 다음과 같다.
1) data segment
- 읽기/쓰기 가능한 메모리 영역
- 프로그램 실행에 필요한 데이터
2) text segment
- 실행 가능한 메모리 영역 (쓰기불가)
- 주로 실행할 명령어들이 들어있다.
(2). C 라이브러리를 사용하지 않고 프로그램 작성하기( original )
3)번 과정 - nasm -f elf -o hello2.o hello2.asm
4)번 과정 - ld -o hello2 hello2.o
한 프로세스가 사용하는 전체 크기는 4GB이다.
리눅스 경우 1G + 3G로 나눈다.
1G는 커널 + 3G는 프로세스이다.
3G 프로세스를 크게 text, data, stack 영역으로 나눈다.
윈도우는 2G + 2G로 나눈다.
2G는 커널 + 2G는 프로세스
segment or pase or section 이라고 부른다.
프로세스를 분산해서 메모리에 올린다.(binding)
그렇다면 운영체제는 무엇을 보고 segment를 분류하는가?
- 실행파일의 시스템 구조를 보고 분류한다.
주로 PE를 보고 확인할 수 있다. elf: 리눅스, pe: 윈도우
'Pwnable' 카테고리의 다른 글
어셈블리 프로그래밍 심화3 (0) | 2018.07.19 |
---|---|
어셈블리 프로그래밍 심화2 (0) | 2018.07.12 |
어셈블리 프로그래밍 심화 및 CPU 구조 (0) | 2018.07.04 |
어셈블리 와 C언어, 리눅스의 이해 (0) | 2018.07.04 |
Understanding of the running process of a program made with C language (0) | 2018.07.02 |