0x281 파일 접근
파일에 접근하는 방법
- File Descriptor - 로우레벨 입출력 함수 사용
- Filestream - 로우레벨 함수로 만들어진 하이레벨 입출력
파일 서술자를 사용하는 네 개의 일반적인 함수 open(), close(), read(), write()
오류 발생시 -1 리턴.
예시
simplenote.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
//fcntl.h와 sys/stat.h에 open()의 플래그가 정의되어 있음
void usage(char *prog_name, char *filename) {
printf("Usage: %s <data to add to %s>\n", prog_name, filename);
exit(0);
}
void fatal(char *); // a function for fatal errors
void *ec_malloc(unsigned int); // an errorchecked malloc() wrapper
int main(int argc, char *argv[]) {
int fd; // file descriptor
char *buffer, *datafile;
buffer = (char *) ec_malloc(100);
datafile = (char *) ec_malloc(20);
strcpy(datafile, "/tmp/notes");
if(argc < 2) // If there aren't commandline arguments
usage(argv[0], datafile); // display usage message and exit
strcpy(buffer, argv[1]); // copy into buffer
printf("[DEBUG] buffer @ %p: \'%s\'\n", buffer, buffer);
printf("[DEBUG] datafile @ %p: \'%s\'\n", datafile, datafile);
strncat(buffer, "\n", 1); // add a newline on the end
// Opening the file
fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
if(fd == -1)
fatal("in main() while opening file");
printf("[DEBUG] file descriptor is %d\n", fd);
// Writing data
if(write(fd, buffer, strlen(buffer)) == -1)
fatal("in main() while writing buffer to file");
//write 함수에는 쓸 바이트 크기가 필요하므로 strlen으로 확인 - strlen은 문자열 길이를 리턴한다
// Closing file
if(close(fd) == -1)
fatal("in main() while closing file");
printf("Note has been saved.\n");
free(buffer);.
free(datafile);
}
// A function to display an error message and then exit
void fatal(char *message) {
char error_message[100];
strcpy(error_message, "[!!] Fatal Error ");
strncat(error_message, message, 83);
perror(error_message); // 종료 전에 오류 출력
exit(-1);
}
// An error checked malloc() wrapper function
void *ec_malloc(unsigned int size) {
void *ptr;
ptr = malloc(size);
if(ptr == NULL)
fatal("in ec_malloc() on memory allocation");
return ptr;
}
open()에서 사용하는 플래그.
- O_RDONLY : 읽기전용
- O_WRONLY : 쓰기전용
- O_RDWR :읽기 쓰기 모두 가능
일부 다른 플래그와 비트 OR 연산자로 결합 가능
- O_APPEND : 파일 끝에 데이터 쓰기
- O_TRUNC : 파일 있으면 내용 지우고 길이를 0으로 만들기
- O_CREAT : 파일이 없으면 생성하기
open()에서 사용한 플래그는 각각 다른 자리수에서 값을 가지므로 OR 을 하면 원래의 값이 보존된다.
예를 들어 O_CREAT는 01000000이고, O_WRONLY는 00000001 이므로 둘을 OR 하면 01000001이 되어 어느 플래그가 값을 가지는지 알 수 있다는 뜻. 자세한 알고리즘은 fcntl_flags에 나와있다.
0x282 파일 권한
O_CREAT가 사용되면 새로 생성되는 파일의 권한을 정의해주는 인자가 필요함. 이 인자는 sys/stat.h에 정의된 비트 OR 연산을 이용해 조합될 수 있는 비트 플래그 사용함.
S_IRUSR - 사용자에게 읽기 권한
S_IWUSR - 사용자에게 쓰기 권한
S_IXUSR - 사용자에게 실행 권한
S_IRGRP - 그룹에
S_IWGRP - 그룹에
S_IXGRP - 그룹에
S_IROTH - 누구나에게
S_IWOTH - 누구나에게
S_IXOTH - 누구나에게
ls -l
하면 나오는 거랑 같음
각 권한은 비트 플래그에 대응. 읽기는 100, 쓰기는 10, 실행은 1이다. 각 값이 고유한 비트를 가지므로 비트 OR로 더한 값으로 권한을 나타낼 수 있음. 이것은 chmod 명령어를 쓸 때 사용된다.
예시
1
chmod 731 test.c
- 7이 나오려면 100+10+1 이므로 사용자에게는 읽기 쓰기 실행 권한 다 줌
- 그룹에게는 10+1=11=3이므로 쓰기, 실행 권한만 줌
- 나머지는 1이므로 읽기 권한만 줌.
1
chmod ugo-wx test.c
- 이거는 u,g,o에서 모두 쓰기 및 실행 권한을 빼라는 명령
1
chmod u+w
사용자에게 쓰기 권한 추가
위 프로그램에서는 사용자에게 읽기, 쓰기 권한을 갖게 하려고 아래와 같이 작성
1
fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR)
0x283 사용자ID
쉘에서 id
라고 치면 아이디를 확인할 수 있음
chsh
명령어는 사용자의 로그인 쉘을 바꾸어 준다.
setuid 플래그가 있는 프로그램이 실행될 때 그 프로그램은 파일 소유자의 사용자 ID로 실행된다.
1
2
3
4
kminito@min-ideapad:~/workspace/c$ which chsh
/usr/bin/chsh
kminito@min-ideapad:~/workspace/c$ ls -l /usr/bin/chsh
-rwsr-xr-x 1 root root 44528 1월 26 2018 /usr/bin/chsh
chsh는 setuid를 가지고 있음.
uid_demo.c
1
2
3
4
5
6
7
8
#include <stdio.h>
int main()
{
printf("real uid: %d\n", getuid());
printf("effective uid: %d\n", geteuid());
}
1
sudo chown root:root ./a.out
-> 소유자와 그룹을 루트로 변경
chsh 사용법
- 사용법 : chsh [옵션] [계정명]
- -s, –shell : 지정하는 셸을 앞으로 사용할 로그인 셸로 바꾼다.
- -l, –list-shells : /etc/shells 파일 안에 지정된 셸을 나열하고 마친다.
- -u, –help : 도움말을 보여준다.
- -v, –version : 버전 정보를 보여주고 마친다.
노트테이커 프로그램 소유권 변경
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
kminito@min-ideapad:~/workspace/art_of$ ls -l notetaker
-rwxr-xr-x 1 kminito kminito 13064 4월 11 21:39 notetaker
kminito@min-ideapad:~/workspace/art_of$ sudo chown root:root ./notetaker
[sudo] kminito의 암호:
kminito@min-ideapad:~/workspace/art_of$ ls -l notetaker
-rwxr-xr-x 1 root root 13064 4월 11 21:39 notetaker
kminito@min-ideapad:~/workspace/art_of$ sudo chmod u+s ./notetaker
kminito@min-ideapad:~/workspace/art_of$ ls -l ./notetaker
-rwsr-xr-x 1 root root 13064 4월 11 21:39 ./notetaker
kminito@min-ideapad:~/workspace/art_of$
kminito@min-ideapad:~/workspace/art_of$ ./notetaker "this is a test of multiuser notes"
[DEBUG] buffer @ 0x5604f5651260:'this is a test of multiuser notes'
[DEBUG] datafile@ 0x5604f56512d0:'/var/notes'
[DEBUG] 파일 서술자는 3
노트가 저장되었습니다.
kminito@min-ideapad:~/workspace/art_of$ cat /var/notes
cat: /var/notes: 허가 거부
kminito@min-ideapad:~/workspace/art_of$ sudo cat /var/notes
�
this is a test of multiuser notes
kminito@min-ideapad:~/workspace/art_of$ sudo hexdump -C /var/notes
00000000 e8 03 00 00 0a 74 68 69 73 20 69 73 20 61 20 74 |.....this is a t|
00000010 65 73 74 20 6f 66 20 6d 75 6c 74 69 75 73 65 72 |est of multiuser|
00000020 20 6e 6f 74 65 73 0a | notes.|
00000027
노트 작성 프로그램의 소유자를 root로 바꾸고 setuid 설정함. 그러면 /var/notes 파일도 루트가 소유하게 됨. 보려면 sudo를 해야 한다.
리틀엔디언이라 나는 아이디 1000이 e8 03으로 표시됨 -> 16진법으로 3e8 = 1000
0x284 구조체
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <time.h>
int main(){
long int seconds_since_epoch;
struct tm current_time, *time_ptr;
int hour, minute, second, day, month, year;
seconds_since_epoch = time(0);
printf("time() ? 기원(1970년1월1일)부터의 초:%ld\n", seconds_since_epoch);
time_ptr = ¤t_time;
localtime_r(&seconds_since_epoch, time_ptr);
// lovaltime_r은 두개의 인자를 받음. 하나는 기원부터의 초, 나머지는 tm 구조체
hour = current_time.tm_hour;
minute = time_ptr->tm_min;
second = *((int *) time_ptr);
// 어떻게 초를 가져오냐면
// 구조체의 주소는 첫 요소를 가리킨다고 한다. 그래서 초를 리턴.
printf("현재 시각: %02d:%02d:%02d\n",hour,minute,second);
}
구조체의 요소들은 메모리상에 바로 이웃해 있으므로 포인터 주소에 바이트를 더해서 직접 접근 가능
int_ptr++;
-> int_ptr에 1을 더하면 주소가 4 증가함.
0x285 함수 포인터
포인터는 함수에도 사용될 수 있다.
1
2
3
4
//
int (*function_ptr)();
function_ptr = func_one;
///
0x286 가상 난수
srand()
함수를 사용해 시드 값을 지정
rand()
함수가 0에서 RAND_MAX 사이의 난수를 리턴