c

스터디 노트 - 0x280 기초 쌓기 (c언어)

Posted on April 11, 2019
티스토리로 블로그 이사중입니다.
http://kminito.tistory.com/ ×

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 사용법

  1. 사용법 : chsh [옵션] [계정명]
  2. -s, –shell : 지정하는 셸을 앞으로 사용할 로그인 셸로 바꾼다.
  3. -l, –list-shells : /etc/shells 파일 안에 지정된 셸을 나열하고 마친다.
  4. -u, –help : 도움말을 보여준다.
  5. -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 = &current_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 사이의 난수를 리턴