저수준 파일 입출력 공부

2018. 4. 29. 02:440x04 pwnable

728x90

저수준 파일 입출력이라는 개념에 대해 알아보았다.


참고 URl : http://12bme.tistory.com/211

정리가 정말 잘되어있다.


우선, 리눅스에서 파일은 일반파일과 특수파일로 구분된다.


1. 일반파일 : 텍스트 바이너리 형태의 데이터를 저장하는 파일

2. 특수파일 : 데이터전송, 장치 접근에 사용하는 파일 


일반파일부터 하나하나 공부해보았다. 


파일을 읽고 쓰는 방법에는 크게 2가지로 나뉘어 진다.


=================

1. 저수준 파일 입출력


2. 고수준 파일 입출력

=================


저수준 파일 입출력 : "커널의 시스템"을 호출하여 파일 입출력을 수행한다.

즉, 어셈블리어가 저급언어이듯 그런 뉘앙스로 '저수준'이라는 명칭이 붙었다고 생각하면 이해하기 쉽다. 

저수준 파일 입출력은 시스템 호출(SYSCALL)을 이용하므로 파일에 빠르게 접근할 수 있다. 

그리고, 바이트 단위로 다룰 수 있기 때문에 "특수파일 역시 읽고 쓸 수 있다" 


장점이 있으면, 당연히 단점도 존재하기 마련이다.

단점은 장점에 의해 생겨난다. 

바이트 단위로 입출력을 수행하기 때문에 어떠한 프로그램을 작성하기 위해 다른 단위가 필요할 경우가 존재하기 마련인데

그때 모듈들을 설계해줘야 한다. 


사람들은 기계어 혹은 어셈블리어를 보는 불편함을 해결하기 위해 고급언어를 만들었듯이,

파일 입출력도 조금이라도 불편함을 해결하기 위해 고수준 파일 입출력이 생겨나게 되었다. 


고수준 파일 입출력은 c언어 표준함수로 제공되고 있으며, "버퍼"를 이용해 읽고 쓰기를 수행하게 된다. 

우리가 평소에 사용하는 단어 "표준 입출력"이 이와 같은 말이다. 


기억해두어야 하는게 있다. 

저수준 파일 입출력은 "파일 기술자/서술자 (file descriptor)"를 이용


고수준 파일 입출력은 "파일 포인터(file pointer)"를 이용한다. 


생각해보는 시간을 가져보자. 

우리가 일반적으로 fopen함수를 이용할 때 반환값이 파일 포인터였는지, 파일 디스크립터였는지. 

그러면 좀 더 쉽게 혹은 좀 더 친근하게 해당 개념에 대해 접근할 수 있을 것이다.


파일 포인터는 무엇일까?

-> 파일 포인터는 "열린 파일의 특성에 관한 정보를 저장하고 있는 구조체"를 가리키는 포인터이다. 


내가 공부하면서 새로 알게 된 사실은 파일 디스크립터가 파일 포인터에 속한다는 것이다. 



생각을 조금 해보았는가?

fopen함수는 함수의 반환형이 FILE* 이다. 그렇다면 fopen은 고수준 파일 입출력이 되는 것이다. 


좀 더 명확하게 fd(file descriptor)와 FILE*에 대해서 알아보았다.


<저수준 파일 입출력>

================================================================

int fd로 보통 선언되어 사용


저수준은 조금 전에 언급하기로 커널의 시스템을 호출하여 파일 입출력을 수행한다고 했다.

그 말인 즉슨, 빠르다는 것이다.

그리고 저수준 파일 입출력은 바이트 단위로 읽고 쓰여진다. 

특수파일에 대한 접근도 용이하다.


주요함수로는 

open, close, read, write, dup, dup2, fcntl, lseek, fsync가 있다. 

================================================================


==================================================================================


<고수준 파일 입출력>

FILE *fp로 보통 선언되어 사용 


우리가 고급언어를 사용하는게 쉽듯, 고수준 파일 입출력은 저수준 파일 입출력보다 사용하기 쉽다.

버퍼 단위로 읽고 쓸수 있다.

버퍼 단위로 하다보니 바이트에 비해 방대한 양을 처리할 수 있기 때문에 데이터 입출력 동기화도 용이하다.


주요함수로는

fopen, fclose, fread, fwrite, fputs, fgets, fprintf, fscanf, freopen, fseek가 있다.

==================================================================================


☆ 알아두면 좋은 것 같아 적어두려 한다. (사실 내가 까먹지 않기 위함) ☆


저수준 파일 입출력에서 사용하는 파일 기술자와 고수준 파일 입출력에서 사용하는 파일 포인터는 상호 변환이 가능하다.


파일 기술자로부터 파일 포인터를 생성할 때 fdopen함수를 사용할 수 있고,

파일 포인터로부터 파일 기술자 정보를 추출할 때 fileno함수를 사용할 수 있다고 한다.

(추출할 때라고 명시되어 있는 이유가 파일 포인터 구조체 내부에 파일 기술자가 담겨 있다는 것을 다시 한번 상기시키기 위함

으로 생각된다)


프로그램을 수행하는 과정에서 데이터를 파일에 임시로 저장해야 할 때가 있다. 그때 기존 파일과 중복되지 않는 

임시 파일명을 만들어주는 함수 (tip이라고 하긴 그렇고 상식적으로 생각했을 때 동일 디렉터리에 같은 파일 이름은 존재할 수 없다.)

를 사용하면 된다고 한다.


임시 파일 명 만들어주는 함수 : tmpnam, tmpname, mktemp

3가지의 함수는 파일명만 리턴하기 때문에 저수준/고수준 파일 생성 함수로 파일을 실제로 생성시켜야 사용가능하다.


본격적으로 저수준 파일 입출력에 대해 정리를 해보는 시간을 가졌다. 


위에서도 언급했지만, 언급을 많이 한다고 나쁠 건 없다고 생각한다.

저수준 파일 입출력은 ! 

"일반 파일"은 물론이고 "특수 파일"에서도 RW가 가능하다.


<파일 기술자>


저수준 파일 입출력 함수는 "파일 기술자"를 사용한다고 했다. 

파일 기술자라는 것은 운영체제가 붙여 둔 번호이다. 운영체제가 왜 굳이 번호를 지정해두었을까? 


지금부터 파일 기술자의 번호에 대해 알아보자.


예전에 아는 실무자 형에게 듣기로 무조건 이 숫자는 아니라고 했지만, 지금 공부하고 있는 건 리눅스 환경이기 때문에

해당 번호로 인지하고 있어도 될 것같다. 


프로그램에서 인덱스가 '0'부터 시작하듯이, 해당 파일 지시자도 0부터 시작한다.

-------------

0은 표준입력 키보드

1은 표준출력 모니터

2는 표준오류출력 모니터  라고 이해하면 편하다.

-------------


해당 블로그에서 프로세스는 동작하면서 필요에 따라 파일을 열고 닫는다고 적혀있는데 아주 잠시 복습 겸 

잡담을 하나 하고 가려한다.


내가 윈도우즈 환경에서 악성코드 분석할 때를 떠올려보면, 하나의 프로세스는 load되었을 때 그 주체 역시 끊임없이 특정 행위를 

반복수행 하였었다. (비단, 악성코드 뿐만 아니라 모든 프로세스들이 그랬다.)

하나의 프로세스가 explorer.exe상에 노출되어 있으면 정적으로 open되어 있는 것처럼 보이긴 하지만

실제로 프로세스를 모니터링 해보면 open -> 기타 여러 행위 (기억이 지금 나지 않는다 query이런것도 있었던 걸로 기억한다) -> close 

이러한 루틴이 지속적으로 일어나는 것을 눈으로 보았기 때문이다. 리눅스 역시 다를바가 없다고 생각한다. 

그리고 이러한 행위는 cpu에서 프로세스에게 할당할 수 있는 자원이 한정적이기 때문에 일어날 수 밖에 없는 일이라고도 

생각한다. 



다시 다른사람의 블로그 내용을 빌려 공부를 해보자. 


상기에 프로세스는 동작하면서 필요에 따라 파일을 열고 닫는다.

하나의 프로세스는 동시에 열수 있는 파일 갯수에 한계가 있는데 굳이 한계를 깨고 싶다면, 커널 설정을 바꾸면 된다고 한다. 


프로세스가 파일을 열 때 파일 기술자는 0번으로 시작한다. 

프로세스가 동작하게 되면, 파일 기술자(0,1,2)는 자동으로 할당이 되게 된다. 

그 말인 즉슨, 프로세스가 '다른파일'을 여는 행위를 하게 되면 파일 기술자 3번에 할당되게 된다고 한다.

이는 오늘 처음 안 사실이다. 


예시를 들어보면, 프로세스에서 open함수를 사용했다면, 이 open함수는 파일 기술자 3번에 할당된다고 생각하면 된다.


파일 관련 행위에서 중요한 2가지가 있다.


첫째, 파일을 RW하기 위해서는 반드시 파일을 열어야 한다. 

둘째, 파일을 다 사용했다면, 메모리 누수방지를 위해 파일을 닫아주어야 한다. 파일을 닫아주게 되면

운영체제는 그 파일 내용을 HDD에 기록하고, 해당 파일이 사용한 버퍼를 반납받게 된다.

그리고, 파일을 사용 후 닫지 않으면 위에서 언급한대로 하나의 프로세스가 열 수 있는 파일은 한계가 있기 때문에 

미처 다른 파일을 열지 못하는 DOS상태가 될 수도 있다.





'0x04 pwnable' 카테고리의 다른 글

Rootme Race condition (ch12)  (0) 2018.05.02
고수준 파일 입출력 공부  (0) 2018.04.29
Protostar Simple Writeup  (0) 2018.04.10
Protostar  (2) 2018.04.10
system함수 릭한 후 /bin/sh 주소 구하기 (NON ASLR)  (0) 2018.04.10