시스템 보안 운영 - 4 (교육 85일차)

SW·2023년 3월 30일
0

실습> 함수 포인터

o 함수란?
함수란 하나 이상의 명령어를 묶어 놓은 것으로 재사용을 하기 위해서 만들어졌다.
함수는 메모리 주소이며 gdb에서 확인이 가능하다.
ex) p 함수명, disas 함수명

o 함수 포인터란?
함수 포인터는 함수의 주소를 가지고 저장하는 포인터 변수이다.

o 함수포인터 사용법
함수포인터의 선언 방법은 아래와 같이 함수의 반환 값과 매개 변수를 잘 맞추면 된다.
형식: 리턴타입 (*포인터변수명)(매개변수1[, ...]);


o 함수 포인터 선언 예
ex1)
void hello() 라는 함수를 가리키는 함수 포인터를 선언한다면 아래와 같이 선언한다.
리턴타입: void, 함수명: hello, 매개변수: 없음
함수포인터 선언 방법: void (*포인터변수명)();
선언된 함수포인터에 함수의 주소를 넣는 방법: 포인터변수명 = 함수명;
void (*p)();
p = hello;
p()

ex2)
int hello(const char *name) 라는 함수를 가리키는 함수 포인터를 선언한다면 아래와 같이 선언한다.
리턴타입: int, 함수명: hello, 매개변수: const char *
함수포인터 선언 방법: int (*포인터변수명)(const char *);
선언된 함수포인터에 함수의 주소를 넣는 방법: 포인터변수명 = 함수명;
void (*func)(const char *);
func = hello;
func("Hello");

실습> 일반 포인터 VS 함수 포인터

1. 일반 포인터 예제
[root@localhost ~]# vi funcPointer1.c 
/*
 * 파일명: funcPointer1.c
 * 프로그램 설명: 함수 포인터를 이해하기 위한 일반 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    // 변수의 포인터
    int a = 7;
    int *ap;

    ap = &a;

    printf(" a: %d, %p\n", a, &a);
    printf("ap: %d, %p\n", *ap, ap); 

    return 0;
}

-Wall: 모든 경고를 출력하는 옵션
-m32: 32bit 컴파일 옵션
-g: gdb 분석 옵션
-o funcPointer1: 실행파일 funcPointer1 를 생성하는 옵션

[root@localhost ~]# gcc -Wall -m32 -g -o funcPointer1 funcPointer1.c 
[root@localhost ~]# ./funcPointer1 
 a: 7, 0xffe20b78
ap: 7, 0xffe20b78

     a                ap
+----------+     +----------+
|    7     | <-- |0xffe20b78|
+----------+     +----------+
|   4byte            4byte
0xffe20b78 


2. 함수 포인터 예제 1
[root@localhost ~]# vi funcPointer2.c
/*
 * 파일명: funcPointer2.c
 * 프로그램 설명: 함수 포인터 1
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

void hello();

int main()
{
    void (*func)(); // 함수 포인터
    func = hello;   // 함수명은 메모리 주소다.
    func();         // 함수 포인터 호출

    return 0;
}

void hello()
{
    printf("Hello ^^*\n");
}

[root@localhost ~]# gdb -q funcPointer2
Reading symbols from /root/funcPointer2...done.
(gdb) b main
(gdb) r
14	    func = hello;   // 함수명은 메모리 주소다.
(gdb) n
15	    func();         // 함수 포인터 호출
(gdb) disas hello
Dump of assembler code for function hello:
   0x0804842b <+0>:	push   %ebp      <-- hello() 함수의 시작 주소
   0x0804842c <+1>:	mov    %esp,%ebp
   0x0804842e <+3>:	sub    $0x18,%esp
   0x08048431 <+6>:	movl   $0x80484d4,(%esp)
   0x08048438 <+13>:	call   0x80482e0 <puts@plt>
   0x0804843d <+18>:	leave  
   0x0804843e <+19>:	ret    
End of assembler dump.

func 변수에는 0x804842b 주소가 저장되어 있다.
(gdb) p func
$1 = (void (*)()) 0x804842b <hello>

(gdb) s
hello () at funcPointer2.c:22
22	    printf("Hello ^^*\n");
(gdb) n
Hello ^^*
23	}
(gdb) n
main () at funcPointer2.c:17
17	    return 0;
(gdb) n
18	}
(gdb) c
(gdb) q

3. 함수 포인터 예제 2
[root@localhost ~]# vi funcPointer3.c
/*
 * 파일명: funcPointer3.c
 * 프로그램 설명: 함수 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

void hello(char *name);

int main()
{
    char realName[] = "Hong gil dong";
    printf("main name: %s\n", realName);  // 배열명은 메모리 주소닷!
    hello(realName);  // 주소를 넘겨주면

    return 0;
}

void hello(char *name)  // 포인터 변수 name이 받았음.
{
    printf("hello name: %s\n", name);
}

수정된 소스코드 
[root@localhost ~]# vi funcPointer3.c 
/*
 * 파일명: funcPointer3.c
 * 프로그램 설명: 함수 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

void hello(const char *name);

int main()
{
    char realName[] = "Hong gil dong";
    void (*funcp)(const char *);

    printf("main name: %s\n", realName);
    hello(realName);

    funcp = hello;  // 함수 포인터
    funcp(realName);

    return 0;
}

void hello(const char *name)
{
    printf("hello name: %s\n", name);
}

[root@localhost ~]# gcc -m32 -g -o funcPointer3 funcPointer3.c 
[root@localhost ~]# ./funcPointer3 
main name: Hong gil dong
hello name: Hong gil dong
hello name: Hong gil dong   

실습> 구조체

1. 소스코드 작성
[root@localhost ~]# vi struct1.c
/*
 * 파일명: struct1.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

/*
 * 배열: 동일한 자료형의 모임. (C언어에 이미 존재하는 자료형)
 * 구조체:
 * 동일하지 않은 자료형의 모임. (사용자가 새롭게 생성하는 자료형)
 * 클래스의 원조가 C언어의 구조체
 *
 * int a;
 *
 * 구조체 정의
 * main() 함수 밖에서 정의한다.
 * 형식:
 * struct 구조체명
 * {
 *     멤버 변수
 *       :
 *       :
 * };
 *
 * 구조체 선언: main() 함수 안에서 선언
 * struct 구조체명 변수명;
 */

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

int main()
{
    // 2. 구조체 변수 선언
    struct point a;

    // 3. 구조체 변수 사용
    // 자료 저장
    a.x = 10;
    a.y = 20;

    // 자료 출력
    printf("a.x = %d\n", a.x);
    printf("a.y = %d\n", a.y);

    return 0;
}

2. 컴파일/실행
[root@localhost ~]# gcc -Wall -m32 -g -o struct1 struct1.c
[root@localhost ~]# ./struct1
a.x = 10
a.y = 20

3. gdb 분석
[root@localhost ~]# gdb -q struct1
Reading symbols from /root/struct1...done.
(gdb) b main
(gdb) r
Breakpoint 1, main () at struct1.c:43
43          a.x = 10;
(gdb) list
38          // 2. 구조체 변수 선언
39          struct point a;
40
41          // 3. 구조체 변수 사용
42          // 자료 저장
43          a.x = 10;
44          a.y = 20;
45
46          // 자료 출력
47          printf("a.x = %d\n", a.x);
(gdb) p a
$1 = {x = 134513771, y = -134430720}
(gdb) n
44          a.y = 20;
(gdb) p a
$2 = {x = 10, y = -134430720}
(gdb) n
47          printf("a.x = %d\n", a.x);
(gdb) p a
$3 = {x = 10, y = 20}
(gdb) n
a.x = 10
48          printf("a.y = %d\n", a.y);
(gdb) n
a.y = 20
50          return 0;
(gdb) c
(gdb) q


[root@localhost ~]# vi struct2.c
/*
 * 파일명: struct2.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct member
{
    char name[30];  // 이름
    char phone[30]; // 전화번호
    int  age;       // 나이
};

int main()
{
    // 2. 구조체 변수 선언
    struct member m;

    // 3. 구조체 변수 사용
    // 저장
    printf(">>> 주소록 프로그램 <<<\n");
    printf("이름: ");
    scanf("%s", m.name);  // m.name == &m.name[0]
    printf("전화번호: ");
    scanf("%s", m.phone); // m.phone == &m.phone[0]
    printf("나이: ");
    scanf("%d", &m.age);

    // 출력
    printf("이름: %s\n", m.name);
    printf("전화번호: %s\n", m.phone);
    printf("나이: %d\n", m.age);

    return 0;
}
[root@localhost ~]# gcc -Wall -g -m32 -o struct2 struct2.c
[root@localhost ~]# ./struct2
>>> 주소록 프로그램 <<<
이름: 홍길동
전화번호: 010-1111-2222
나이: 30
이름: 홍길동
전화번호: 010-1111-2222
나이: 30

[root@localhost ~]# gdb -q struct2
Reading symbols from /root/struct2...done.
(gdb) b main
Breakpoint 1 at 0x8048496: file struct2.c, line 24.
(gdb) r
Starting program: /root/struct2

Breakpoint 1, main () at struct2.c:24
24          printf(">>> 주소록 프로그램 <<<\n");
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) p m
$1 = {
  name = "\377\377\377\377\030\326\377\377x\035\341\367\025\203\004\b\000\000\000\000\000\000\000\000\000\240\004\b\242\205",
  phone = "\004\b\001\000\000\000\324\326\377\377\334\326\377\377\355y\343\367\304\303\374\367\000\300\000\000[\205\004\b", age = -134430720}
(gdb) n
>>> 주소록 프로그램 <<<
25          printf("이름: ");
(gdb) n
26          scanf("%s", m.name);  // m.name == &m.name[0]
(gdb) HongGilDong
Undefined command: "HongGilDong".  Try "help".
(gdb) n
이름: HongGilDong
27          printf("전화번호: ");
(gdb) n
28          scanf("%s", m.phone); // m.phone == &m.phone[0]
(gdb) n
전화번호: 010-1111-2222
29          printf("나이: ");
(gdb) n
30          scanf("%d", &m.age);
(gdb) n
나이: 30
33          printf("이름: %s\n", m.name);
(gdb) p m
$2 = {
  name = "HongGilDong\000\025\203\004\b\000\000\000\000\000\000\000\000\000\240\004\b\242\205", phone = "010-1111-2222\000\355y\343\367\304\303\374\367\000\300\000\000[\205\004\b",
  age = 30}
(gdb) x/16xw &m
0xffffd5f0:     0x676e6f48      0x446c6947      0x00676e6f      0x08048315
0xffffd600:     0x00000000      0x00000000      0x0804a000      0x313085a2
0xffffd610:     0x31312d30      0x322d3131      0x00323232      0xf7e379ed
0xffffd620:     0xf7fcc3c4      0x0000c000      0x0804855b      0x0000001e
(gdb) x/16c &m
0xffffd5f0:     72 'H'  111 'o' 110 'n' 103 'g' 71 'G'  105 'i' 108 'l' 68 'D'
0xffffd5f8:     111 'o' 110 'n' 103 'g' 0 '\000'        21 '\025'       -125 '\203'     4 '\004'      8 '\b'
(gdb) n
이름: HongGilDong
34          printf("전화번호: %s\n", m.phone);
(gdb) n
전화번호: 010-1111-2222
35          printf("나이: %d\n", m.age);
(gdb) n
나이: 30
37          return 0;
(gdb) p m.name
$3 = "HongGilDong\000\025\203\004\b\000\000\000\000\000\000\000\000\000\240\004\b\242\205"
(gdb) x/s m.name
0xffffd5f0:     "HongGilDong"
(gdb) p m.phone
$4 = "010-1111-2222\000\355y\343\367\304\303\374\367\000\300\000\000[\205\004\b"
(gdb) x/s m.phone
0xffffd60e:     "010-1111-2222"
(gdb) x/xw m.age
0x1e:   Cannot access memory at address 0x1e
(gdb) x/xw &m.age
0xffffd62c:     0x0000001e
(gdb) shell
[root@localhost ~]# python -c 'print(int(0x1e))'
30
[root@localhost ~]# exit

(gdb) c
Continuing.
[Inferior 1 (process 105043) exited normally]
(gdb) q


[root@localhost ~]# vi struct3.c
/*
 * 파일명: struct3.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

int main()
{
    // 2. 구조체 변수 선언
    struct point a;
    char ch;
    int i;
    double d;

    // 메모리 크기 출력
    printf(" a: %d\n", sizeof(a));   // 8byte
    printf("ch: %d\n", sizeof(ch));  // 1byte
    printf(" i: %d\n", sizeof(i));   // 4byte
    printf(" d: %d\n", sizeof(d));   // 8byte

    return 0;
}

[root@localhost ~]# gcc -Wall -m32 -g -o struct3 struct3.c
[root@localhost ~]# ./struct3
 a: 8
ch: 1
 i: 4
 d: 8


[root@localhost ~]# vi struct4.c
/*
 * 파일명: struct4.c
 * 프로그램 설명: 구조체 크기 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;     // 4byte
    int y;     // 4byte
    char ch;   // 1byte
    double d;  // 8byte
};

int main()
{
    // 2. 구조체 변수 선언
    struct point a;

    // 메모리 크기 출력
    // 구조체의 크기는 반드시 sizeof(구조체변수명)으로 구해야 한다.
    printf("a: %d\n", sizeof(a));   // 17byte가 아닌 20byte가 나온다.

    return 0;
}

[root@localhost ~]# gcc -Wall -g -m32 -o struct4 struct4.c
[root@localhost ~]# ./struct4
a: 20


[root@localhost ~]# vi struct5.c
/*
 * 파일명: struct5.c
 * 프로그램 설명: 구조체 크기 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>
#include <string.h>

// 1. 구조체 정의
struct point
{
    int x;     // 4byte
    int y;     // 4byte
    char ch;   // 1byte
    double d;  // 8byte
    char name[20]; // 20byte

};

int main()
{
    // 2. 구조체 변수 선언
    struct point a;
    // memset: 메모리를 초기화할 때 사용하는 함수
    // void *memset(void *s, int c, size_t n);
    memset((struct point *) &a, 0, sizeof(a));

    return 0;
}

[root@localhost ~]# gcc -Wall -g -m32 -o struct5 struct5.c
[root@localhost ~]# gdb -q struct5
Reading symbols from /root/struct5...done.
(gdb) b main
Breakpoint 1 at 0x8048416: file struct5.c, line 26.
(gdb) r
Starting program: /root/struct5

Breakpoint 1, main () at struct5.c:26
26          memset((struct point *) &a, 0, sizeof(a));
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686

메모리를 초기화 하기 전
- 쓰레기값이 들어가 있다.
(gdb) p a
$1 = {x = 134520832, y = 134513810, ch = 1 '\001', d = -nan(0xfd6dcffffd6d4),
  name = "\355y\343\367\304\303\374\367\000\300\000\000K\204\004\b\000\300\374", <incomplete sequence \367>}

메모리를 조사한다.
(gdb) x/28xw &a
0xffffd608:     0x0804a000      0x08048492      0x00000001      0xffffd6d4
0xffffd618:     0xffffd6dc      0xf7e379ed      0xf7fcc3c4      0x0000c000
0xffffd628:     0x0804844b      0xf7fcc000      0x08048440      0x00000000
0xffffd638:     0x00000000      0xf7e1f2d3      0x00000001      0xffffd6d4
0xffffd648:     0xffffd6dc      0xf7fd86b0      0x00000001      0x00000001
0xffffd658:     0x00000000      0x0804a00c      0x0804821c      0xf7fcc000
0xffffd668:     0x00000000      0x00000000      0x00000000      0x1428cf56

memset() 함수를 실행해서 a 변수의 메모리를 0으로 초기화 한다.
(gdb) n
28          return 0;

(gdb) p a
$2 = {x = 0, y = 0, ch = 0 '\000', d = 0, name = '\000' <repeats 19 times>}

(gdb) p sizeof(a)
$3 = 40

4byte * 10 개 = 40byte가 모두 0으로 초기화 되었다.
(gdb) x/28xw &a
0xffffd608:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd618:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd628:     0x00000000      0x00000000      0x08048440      0x00000000
0xffffd638:     0x00000000      0xf7e1f2d3      0x00000001      0xffffd6d4
0xffffd648:     0xffffd6dc      0xf7fd86b0      0x00000001      0x00000001
0xffffd658:     0x00000000      0x0804a00c      0x0804821c      0xf7fcc000
0xffffd668:     0x00000000      0x00000000      0x00000000      0x1428cf56

(gdb) c
(gdb) q


[root@localhost ~]# vi pointer1.c
#include <stdio.h>

int main()
{
    int i = 10;
    int *p;

    p = &i;
    return 0;
}

[root@localhost ~]# gcc -g -m32 -o pointer1 pointer1.c
[root@localhost ~]# gdb -q pointer1
Reading symbols from /root/pointer1...done.
(gdb) b main
Breakpoint 1 at 0x80483e3: file pointer1.c, line 5.
(gdb) r
5           int i = 10;
(gdb) n
8           p = &i;
(gdb) n
9           return 0;

(gdb) p i
$1 = 10
(gdb) p &i
$2 = (int *) 0xffffd630

(gdb) p p
$3 = (int *) 0xffffd630

(gdb) x/xw &i
0xffffd630:     0x0000000a

(gdb) p &p
$4 = (int **) 0xffffd634

(gdb) x/xw &p
0xffffd634:     0xffffd630  <-- p = &i; 이므로 i의 주소가 저장되어 있다.
(gdb) c
(gdb) q


[root@localhost ~]# vi struct6.c
/*
 * 파일명: struct6.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

int main()
{
    // 2. 구조체 변수 선언
    struct point a;
    struct point *ap;

    // 3. 구조체 변수 사용
    // 저장
    a.x = 10;
    a.y = 20;

    ap = &a;


    // 출력 1
    // a에서 멤버 변수에 접근하는 형식: a.멤버변수명
    printf("a.x = %d\n", a.x);
    printf("a.y = %d\n", a.y);

    // 출력 2
    // ap에서 멤버 변수에 접근하는 형식: ap->멤버변수명
    printf("ap->x = %d\n", ap->x);
    printf("ap->y = %d\n", ap->y);

    return 0;
}

[root@localhost ~]# gcc -Wall -g -m32 -o struct6 struct6.c
[root@localhost ~]# ./struct6
a.x = 10
a.y = 20
ap->x = 10
ap->y = 20

[root@localhost ~]# gdb struct6
(gdb) b main
Breakpoint 1 at 0x8048416: file struct6.c, line 24.
(gdb) r
Starting program: /root/struct6

Breakpoint 1, main () at struct6.c:24
24          a.x = 10;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) disp a
1: a = {x = 49152, y = 134513819}
(gdb) disp ap
2: ap = (struct point *) 0xf7fcc000
(gdb) n
25          a.y = 20;
2: ap = (struct point *) 0xf7fcc000
1: a = {x = 10, y = 134513819}
(gdb) n
27          ap = &a;
2: ap = (struct point *) 0xf7fcc000
1: a = {x = 10, y = 20}
(gdb) n
30          printf("a.x = %d\n", a.x);
2: ap = (struct point *) 0xffffd624
1: a = {x = 10, y = 20}
(gdb) p &a
$1 = (struct point *) 0xffffd624
(gdb) p ap
$2 = (struct point *) 0xffffd624
(gdb) p ap->x
$3 = 10
(gdb) p ap->y
$4 = 20
(gdb) p ap.x
$5 = 10
(gdb) p ap.y
$6 = 20
(gdb) p a.x
$7 = 10
(gdb) p a.y
$8 = 20
(gdb) p sizeof(a)
$9 = 8
(gdb) p sizeof(ap)
$10 = 4
(gdb) x/xw ap
0xffffd624:     0x0000000a
(gdb) x/xw &ap
0xffffd62c:     0xffffd624
(gdb) p &a
$11 = (struct point *) 0xffffd624
(gdb) c
Continuing.
a.x = 10
a.y = 20
ap->x = 10
ap->y = 20
[Inferior 1 (process 105697) exited normally]
(gdb) q


[root@localhost ~]# vi struct7.c
/*
 * 파일명: struct7.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

void printStruct1(struct point a);
void printStruct2(struct point *ap);

int main()
{
    // 2. 구조체 변수 선언
    struct point a;

    // 3. 구조체 변수 사용
    // 저장
    a.x = 10;
    a.y = 20;

    // 출력 1
    printStruct1(a);

    // 출력 2
    printStruct2(&a);  // ap = &a;

    return 0;
}

void printStruct1(struct point a)
{
    // a에서 멤버 변수에 접근하는 형식: a.멤버변수명
    printf("a.x = %d\n", a.x);
    printf("a.y = %d\n", a.y);
}

void printStruct2(struct point *ap)
{
    // ap에서 멤버 변수에 접근하는 형식: ap->멤버변수명
    printf("ap->x = %d\n", ap->x);
    printf("ap->y = %d\n", ap->y);
}

[root@localhost ~]# gcc -Wall -g -m32 -o struct7 struct7.c
[root@localhost ~]# ./struct7
a.x = 10
a.y = 20
ap->x = 10
ap->y = 20

Call by value를 활용한 소스코드 수정
[root@localhost ~]# vi struct8.c
/*
 * 파일명: struct8.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

void printStruct1(struct point a);
void printStruct2(struct point *ap);

int main()
{
    // 2. 구조체 변수 선언
    struct point a;

    // 3. 구조체 변수 사용
    // 저장
    a.x = 10;
    a.y = 20;

    // Call by value
    // 값에 의한 호출: 
    // 값에 의한 호출은 다른 함수에서는 원본의 값을 변경할 수 없다.
    printStruct1(a);
    printf("main() a.x = %d\n", a.x);  // 10
    printf("main() a.y = %d\n", a.y);  // 20

    // 출력 2
    // printStruct2(&a);  // ap = &a;

    return 0;
}

void printStruct1(struct point a)
{
    // a에서 멤버 변수에 접근하는 형식: a.멤버변수명
    printf("printStruct1() a.x = %d\n", a.x);  // 10
    printf("printStruct1() a.y = %d\n", a.y);  // 20
    a.x += 5;
    a.y += 5;
    printf("printStruct1() a.x = %d\n", a.x);  // 15
    printf("printStruct1() a.y = %d\n", a.y);  // 25
}

void printStruct2(struct point *ap)
{
    // ap에서 멤버 변수에 접근하는 형식: ap->멤버변수명
    printf("ap->x = %d\n", ap->x);
    printf("ap->y = %d\n", ap->y);
}

[root@localhost ~]# gcc -Wall -g -m32 -o struct8 struct8.c
[root@localhost ~]# ./struct8
printStruct1() a.x = 10
printStruct1() a.y = 20
printStruct1() a.x = 15
printStruct1() a.y = 25
main() a.x = 10
main() a.y = 20

gdb로 분석하기
[root@localhost ~]# gdb -q struct8
Reading symbols from /root/struct8...done.
(gdb) b main
(gdb) r
Breakpoint 1, main () at struct8.c:26
26          a.x = 10;
(gdb) n
27          a.y = 20;
(gdb) n
31          printStruct1(a);
(gdb) p a
$1 = {x = 10, y = 20}
(gdb) p &a
$2 = (struct point *) 0xffffd628
(gdb) disp &a
1: &a = (struct point *) 0xffffd628
(gdb) x/2xw 0xffffd628
0xffffd628:     0x0000000a      0x00000014
(gdb) shell
[root@localhost ~]# python -c 'print 0x14'
20
[root@localhost ~]# python -c 'print 0x0a'
10
[root@localhost ~]# exit
exit
(gdb) s
printStruct1 (a=...) at struct8.c:44
44          printf("printStruct1() a.x = %d\n", a.x);  // 10
(gdb) p a
$3 = {x = 10, y = 20}
(gdb) p &a
$4 = (struct point *) 0xffffd610
(gdb) n
printStruct1() a.x = 10
45          printf("printStruct1() a.y = %d\n", a.y);  // 20
(gdb) n
printStruct1() a.y = 20
46          a.x += 5;
(gdb) p a
$5 = {x = 10, y = 20}
(gdb) n
47          a.y += 5;
(gdb) n
48          printf("printStruct1() a.x = %d\n", a.x);  // 15
(gdb) p a
$6 = {x = 15, y = 25}
(gdb) n
printStruct1() a.x = 15
49          printf("printStruct1() a.y = %d\n", a.y);  // 25
(gdb) n
printStruct1() a.y = 25
50      }
(gdb) p &a
$7 = (struct point *) 0xffffd610
(gdb) n
main () at struct8.c:32
32          printf("main() a.x = %d\n", a.x);  // 10
1: &a = (struct point *) 0xffffd628
(gdb) p &a
$8 = (struct point *) 0xffffd628
(gdb) p a
$9 = {x = 10, y = 20}
(gdb) n
main() a.x = 10
33          printf("main() a.y = %d\n", a.y);  // 20
1: &a = (struct point *) 0xffffd628
(gdb) n
main() a.y = 20
38          return 0;
1: &a = (struct point *) 0xffffd628
(gdb) c
Continuing.
[Inferior 1 (process 105805) exited normally]
(gdb) q


Call by reference를 활용한 소스코드 수정
[root@localhost ~]# vi struct8.c
/*
 * 파일명: struct8.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

void printStruct1(struct point a);
void printStruct2(struct point *ap);

int main()
{
    // 2. 구조체 변수 선언
    struct point a;

    // 3. 구조체 변수 사용
    // 저장
    a.x = 10;
    a.y = 20;


    // Call by value
    // 값에 의한 호출:
    // 값에 의한 호출은 다른 함수에서는 원본의 값을 변경할 수 없다.
    //printStruct1(a);
    //printf("main() a.x = %d\n", a.x);  // 10
    //printf("main() a.y = %d\n", a.y);  // 20

    // 출력 2
    // Call by reference
    // 참조에 의한 호출
    // 참조에 의한 호출은 다른 함수에서 원본의 값을 변경할 수 있다.
    printStruct2(&a);  // ap = &a;
    printf("main() a.x = %d\n", a.x);  // 15
    printf("main() a.y = %d\n", a.y);  // 25

    return 0;
}

void printStruct1(struct point a)
{
    // a에서 멤버 변수에 접근하는 형식: a.멤버변수명
    printf("printStruct1() a.x = %d\n", a.x);  // 10
    printf("printStruct1() a.y = %d\n", a.y);  // 20
    a.x += 5;
    a.y += 5;
    printf("printStruct1() a.x = %d\n", a.x);  // 15
    printf("printStruct1() a.y = %d\n", a.y);  // 25
}

void printStruct2(struct point *ap)
{
    // ap에서 멤버 변수에 접근하는 형식: ap->멤버변수명
    printf("printStruct2() ap->x = %d\n", ap->x);  // 10
    printf("printStruct2() ap->y = %d\n", ap->y);  // 20
    ap->x += 5;  // 15
    ap->y += 5;  // 25
    printf("printStruct2() ap->x = %d\n", ap->x);  // 15
    printf("printStruct2() ap->y = %d\n", ap->y);  // 25
}

[root@localhost ~]# gcc -Wall -g -m32 -o struct8 struct8.c
[root@localhost ~]# ./struct8
printStruct2() ap->x = 10
printStruct2() ap->y = 20
printStruct2() ap->x = 15
printStruct2() ap->y = 25
main() a.x = 15
main() a.y = 25



[root@localhost ~]# gdb -q struct8
Reading symbols from /root/struct8...done.
(gdb) b main
Breakpoint 1 at 0x8048416: file struct8.c, line 26.
(gdb) r
Starting program: /root/struct8

Breakpoint 1, main () at struct8.c:26
26          a.x = 10;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) p a
$1 = {x = 134514011, y = -134430720}
(gdb) disp a
1: a = {x = 134514011, y = -134430720}
(gdb) n
27          a.y = 20;
1: a = {x = 10, y = -134430720}
(gdb) n
41          printStruct2(&a);  // ap = &a;
1: a = {x = 10, y = 20}
(gdb) p &p
No symbol "p" in current context.
(gdb) p &a
$2 = (struct point *) 0xffffd628
(gdb) s
printStruct2 (ap=0xffffd628) at struct8.c:62
62          printf("printStruct2() ap->x = %d\n", ap->x);  // 10
(gdb) p ap
$3 = (struct point *) 0xffffd628
(gdb) p ap
$4 = (struct point *) 0xffffd628
(gdb) p *ap
$5 = {x = 10, y = 20}
(gdb) p ap->x
$6 = 10
(gdb) p ap->y
$7 = 20
(gdb) disp *ap
2: *ap = {x = 10, y = 20}
(gdb) n
printStruct2() ap->x = 10
63          printf("printStruct2() ap->y = %d\n", ap->y);  // 20
2: *ap = {x = 10, y = 20}
(gdb) n
printStruct2() ap->y = 20
64          ap->x += 5;  // 15
2: *ap = {x = 10, y = 20}
(gdb) n
65          ap->y += 5;  // 25
2: *ap = {x = 15, y = 20}
(gdb) n
66          printf("printStruct2() ap->x = %d\n", ap->x);  // 15
2: *ap = {x = 15, y = 25}
(gdb) n
printStruct2() ap->x = 15
67          printf("printStruct2() ap->y = %d\n", ap->y);  // 25
2: *ap = {x = 15, y = 25}
(gdb) n
printStruct2() ap->y = 25
68      }
2: *ap = {x = 15, y = 25}
(gdb) n
main () at struct8.c:42
42          printf("main() a.x = %d\n", a.x);  // 15
1: a = {x = 15, y = 25}
(gdb) n
main() a.x = 15
43          printf("main() a.y = %d\n", a.y);  // 25
1: a = {x = 15, y = 25}
(gdb) n
main() a.y = 25
45          return 0;
1: a = {x = 15, y = 25}
(gdb) c
Continuing.
[Inferior 1 (process 105839) exited normally]
(gdb) q

실습> 구조체를 활용한 사용자 검색 프로그램

1. 소스코드 작성
[root@localhost ~]# vi linuxUserInfo.c
/*
 * 파일명: linuxUserInfo.c
 * 프로그램 설명: CentOS7에서 사용자 정보를 검색한다.
 * 작성자: 리눅스마스터넷
 *
 * 64bit 컴파일: gcc -Wall -g -o linuxUserInfo linuxUserInfo.c
 * 32bit 컴파일: gcc -Wall -m32 -g -o linuxUserInfo linuxUserInfo.c
 */
#include <sys/types.h>
#include <pwd.h>
#include <stdio.h>

/*
 * /etc/passwd
 * root:x:0:0:root:/root:/bin/bash
 *
 * The passwd structure.
 * struct passwd
 * {
 *     char *pw_name;           // Username.
 *     char *pw_passwd;         // Password.
 *     __uid_t pw_uid;          // User ID.
 *     __gid_t pw_gid;          // Group ID.
 *     char *pw_gecos;          // Real name.
 *     char *pw_dir;            // Home directory.
 *     char *pw_shell;          // Shell program.
 * };
 */

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        fprintf(stderr, "사용법: %s username\n", argv[0]);
        return 1;
    }

    // 사용자를 /etc/passwd에서 검색한다.
    struct passwd *pw = getpwnam(argv[1]);

    if(pw == NULL) { // 검색에 실패하면 프로세스를 종료한다.
        fprintf(stderr, "%s 사용자가 존재하지 않습니다.\n", argv[1]);
        return 1;
    }

    // 검색에 성공하면 사용자 정보를 출력한다.
    printf(">>> 검색된 사용자 정보 <<<\n"
           "Username: %s\n"
           "Password: %s\n"
           "User ID: %d\n"
           "Group ID: %d\n"
           "Real name: %s\n"
           "Home directory: %s\n"
           "Shell program: %s\n",
           pw->pw_name, pw->pw_passwd, pw->pw_uid,
           pw->pw_gid,  pw->pw_gecos,  pw->pw_dir, pw->pw_shell);

    return 0;
}

2. 실행
[root@localhost ~]# gcc -Wall -g -m32 -o linuxUserInfo linuxUserInfo.c
[root@localhost ~]# ./linuxUserInfo
사용법: ./linuxUserInfo username
[root@localhost ~]# ./linuxUserInfo root
>>> 검색된 사용자 정보 <<<
Username: root
Password: x
User ID: 0
Group ID: 0
Real name: root
Home directory: /root
Shell program: /bin/bash
[root@localhost ~]# ./linuxUserInfo user1
>>> 검색된 사용자 정보 <<<
Username: user1
Password: x
User ID: 1001
Group ID: 100
Real name:
Home directory: /home/user1
Shell program: /bin/bash

3. gdb 분석
[root@localhost ~]# gdb -q linuxUserInfo
Reading symbols from /root/linuxUserInfo...done.
(gdb) b main
(gdb) set args root
(gdb) r
Breakpoint 1, main (argc=2, argv=0xffffd6c4) at linuxUserInfo.c:32
32          if(argc != 2)
(gdb) n
39          struct passwd *pw = getpwnam(argv[1]);
(gdb) n
41          if(pw == NULL) { // 검색에 실패하면 프로세스를 종료한다.
(gdb) disp pw
1: pw = (struct passwd *) 0xf7fcdce0 <resbuf.9415>
(gdb) p *pw
$7 = {pw_name = 0x804b008 "root", pw_passwd = 0x804b00d "x", pw_uid = 0, pw_gid = 0,
  pw_gecos = 0x804b013 "root", pw_dir = 0x804b018 "/root", pw_shell = 0x804b01e "/bin/bash"}
(gdb) c
Continuing.
>>> 검색된 사용자 정보 <<<
Username: root
Password: x
User ID: 0
Group ID: 0
Real name: root
Home directory: /root
Shell program: /bin/bash
(gdb) q

실습> 구조체를 활용한 그룹 검색 프로그램

1. 소스코드 작성
[root@localhost ~]# cat linuxGroupInfo.c
/*
 * 파일명: linuxGroupInfo.c
 * 프로그램 설명: CentOS7에서 그룹 정보를 검색한다.
 * 작성자: 리눅스마스터넷
 *
 * 64bit 컴파일: gcc -Wall -g -o linuxGroupInfo linuxGroupInfo.c
 * 32bit 컴파일: gcc -Wall -m32 -g -o linuxGroupInfo linuxGroupInfo.c
 */
#include <sys/types.h>
#include <grp.h>
#include <stdio.h>

// struct group *getgrnam(const char *name);
//
/* The group structure.
 *
 * /etc/group
 * root:x:0:
 *
 * struct group
 * {
 *   char *gr_name;     // Group name.
 *   char *gr_passwd;   // Password.
 *   __gid_t gr_gid;    // Group ID.
 *   char **gr_mem;     // Member list.
 * };
 */

int main(int argc, char *argv[])
{
    int i = 0;

    if(argc != 2) {
        fprintf(stderr, "사용법: %s groupname\n", argv[0]);
        return 1;
    }

    // 그룹을 /etc/group에서 검색한다.
    struct group *gr = getgrnam(argv[1]);

    if(gr == NULL) { // 검색에 실패하면 프로세스를 종료한다.
        fprintf(stderr, "%s 그룹이 존재하지 않습니다.\n", argv[1]);
        return 1;
    }

    // 검색에 성공하면 그룹 정보를 출력한다.
    printf(">>> 검색된 그룹 정보 <<<\n"
           "Groupname: %s\n"
           "Password: %s\n"
           "Group ID: %d\n"
           "Memberlist : ", gr->gr_name, gr->gr_passwd, gr->gr_gid);

    // group 구조체의 gr_mem 이 이중 포인터이므로 배열을 이용해서 반복문을 이용한다.
    while (gr->gr_mem[i] != NULL)
        printf("%s ", gr->gr_mem[i++]);

    putchar('\n');

    return 0;
}

2. 실행
[root@localhost ~]# gcc -Wall -m32 -g -o linuxGroupInfo linuxGroupInfo.c
[root@localhost ~]# ./linuxGroupInfo
사용법: ./linuxGroupInfo groupname
[root@localhost ~]# ./linuxGroupInfo root
>>> 검색된 그룹 정보 <<<
Groupname: root
Password: x
Group ID: 0
Memberlist :

[root@localhost ~]# groupadd -g 5000 testgroup
[root@localhost ~]# useradd test1 -G testgroup
[root@localhost ~]# useradd test2 -G testgroup
[root@localhost ~]# useradd test3 -G testgroup
[root@localhost ~]# tail -5 /etc/group
user3:x:2001:
testgroup:x:5000:test1,test2,test3
test1:x:3001:
test2:x:3002:
test3:x:3003:

[root@localhost ~]# ./linuxGroupInfo testgroup
>>> 검색된 그룹 정보 <<<
Groupname: testgroup
Password: x
Group ID: 5000
Memberlist : test1 test2 test3

3. gdb 분석
[root@localhost ~]# gdb -q linuxGroupInfo
Reading symbols from /root/linuxGroupInfo...done.
(gdb) b main
Breakpoint 1 at 0x80484d6: file linuxGroupInfo.c, line 31.
(gdb) set args root
(gdb) r
Starting program: /root/linuxGroupInfo root

Breakpoint 1, main (argc=2, argv=0xffffd6c4) at linuxGroupInfo.c:31
31          int i = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) n
33          if(argc != 2) {
(gdb) n
39          struct group *gr = getgrnam(argv[1]);
(gdb) n
41          if(gr == NULL) { // 검색에 실패하면 프로세스를 종료한다.
(gdb) p *gr
$1 = {gr_name = 0x804b008 "root", gr_passwd = 0x804b00d "x", gr_gid = 0, gr_mem = 0x804b014}
(gdb) x/8xw gr->gr_name
0x804b008:      0x746f6f72      0x30007800      0x0000003a      0x00000000
0x804b018:      0x00000000      0x00000000      0x00000000      0x00000000
(gdb) c
Continuing.
>>> 검색된 그룹 정보 <<<
Groupname: root
Password: x
Group ID: 0
Memberlist :
[Inferior 1 (process 106042) exited normally]

Memberlist 에 여러 개 있는 그룹을 확인한다.
- 이중 포인터로 되어 있다.
(gdb) r testgroup
Starting program: /root/linuxGroupInfo testgroup

Breakpoint 1, main (argc=2, argv=0xffffd6c4) at linuxGroupInfo.c:31
31          int i = 0;
(gdb) n
33          if(argc != 2) {
(gdb) n
39          struct group *gr = getgrnam(argv[1]);
(gdb) n
41          if(gr == NULL) { // 검색에 실패하면 프로세스를 종료한다.
(gdb) p *gr
$7 = {gr_name = 0x804b008 "testgroup", gr_passwd = 0x804b012 "x", gr_gid = 5000,
  gr_mem = 0x804b02c}
(gdb) x/4xw 0x804b02c
0x804b02c:      0x0804b019      0x0804b01f      0x0804b025      0x00000000
(gdb) x/s 0x0804b019
0x804b019:      "test1"
(gdb) x/s 0x0804b01f
0x804b01f:      "test2"
(gdb) x/s 0x0804b025
0x804b025:      "test3"
(gdb) c
Continuing.
>>> 검색된 그룹 정보 <<<
Groupname: testgroup
Password: x
Group ID: 5000
Memberlist : test1 test2 test3
[Inferior 1 (process 106064) exited normally]
(gdb) q

실습> 구조체에 함수의 주소 저장하기

1. 소스코드 작성
[root@localhost ~]# cat struct9.c
/*
 * 파일명: struct9.c
 * 프로그램 설명: 구조체 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 1. 구조체 정의
struct point
{
    int x;
    int y;
};

struct point_module
{
    void (*p1)(struct point);
    void (*p2)(struct point *);
};

void printStruct1(struct point a);
void printStruct2(struct point *ap);

int main()
{
    // 2. 구조체 변수 선언
    struct point a;

    // 3. 구조체 변수 사용
    // 저장
    a.x = 10;
    a.y = 20;

    struct point_module pm;
    pm.p1 = printStruct1;
    pm.p2 = printStruct2;

    // 출력 1
    pm.p1(a);
    //printStruct1(a);

    // 출력 2
    pm.p2(&a);
    //printStruct2(&a);  // ap = &a;

    return 0;
}

void printStruct1(struct point a)
{
    // a에서 멤버 변수에 접근하는 형식: a.멤버변수명
    printf("a.x = %d\n", a.x);
    printf("a.y = %d\n", a.y);
}

void printStruct2(struct point *ap)
{
    // ap에서 멤버 변수에 접근하는 형식: ap->멤버변수명
    printf("ap->x = %d\n", ap->x);
    printf("ap->y = %d\n", ap->y);
}

2. 컴파일 실행
[root@localhost ~]# gcc -Wall -m32 -g -o struct9 struct9.c
[root@localhost ~]# ./struct9
a.x = 10
a.y = 20
ap->x = 10
ap->y = 20

3. gdb 분석
[root@localhost ~]# gdb -q struct9
Reading symbols from /root/struct9...done.
(gdb) b main
Breakpoint 1 at 0x8048416: file struct9.c, line 32.
(gdb) r
Starting program: /root/struct9

Breakpoint 1, main () at struct9.c:32
32          a.x = 10;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.i686
(gdb) n
33          a.y = 20;
(gdb) n
36          pm.p1 = printStruct1;
(gdb) n
37          pm.p2 = printStruct2;
(gdb) n
40          pm.p1(a);
(gdb) p pm
$1 = {p1 = 0x804845f <printStruct1>, p2 = 0x804848d <printStruct2>}
(gdb) x/xw &pm.p1
0xffffd620:     0x0804845f
(gdb) x/xw &pm.p2
0xffffd624:     0x0804848d
(gdb) disas printStruct1
Dump of assembler code for function printStruct1:
   0x0804845f <+0>:     push   %ebp   <-- printStruct1 시작 주소
   0x08048460 <+1>:     mov    %esp,%ebp
   0x08048462 <+3>:     sub    $0x18,%esp
   0x08048465 <+6>:     mov    0x8(%ebp),%eax
   0x08048468 <+9>:     mov    %eax,0x4(%esp)
   0x0804846c <+13>:    movl   $0x8048554,(%esp)
   0x08048473 <+20>:    call   0x80482e0 <printf@plt>
   0x08048478 <+25>:    mov    0xc(%ebp),%eax
   0x0804847b <+28>:    mov    %eax,0x4(%esp)
   0x0804847f <+32>:    movl   $0x804855e,(%esp)
   0x08048486 <+39>:    call   0x80482e0 <printf@plt>
   0x0804848b <+44>:    leave
   0x0804848c <+45>:    ret
End of assembler dump.
(gdb) disas printStruct2
Dump of assembler code for function printStruct2:
   0x0804848d <+0>:     push   %ebp    <-- printStruct2 시작 주소
   0x0804848e <+1>:     mov    %esp,%ebp
   0x08048490 <+3>:     sub    $0x18,%esp
   0x08048493 <+6>:     mov    0x8(%ebp),%eax
   0x08048496 <+9>:     mov    (%eax),%eax
   0x08048498 <+11>:    mov    %eax,0x4(%esp)
   0x0804849c <+15>:    movl   $0x8048568,(%esp)
   0x080484a3 <+22>:    call   0x80482e0 <printf@plt>
   0x080484a8 <+27>:    mov    0x8(%ebp),%eax
   0x080484ab <+30>:    mov    0x4(%eax),%eax
   0x080484ae <+33>:    mov    %eax,0x4(%esp)
   0x080484b2 <+37>:    movl   $0x8048574,(%esp)
   0x080484b9 <+44>:    call   0x80482e0 <printf@plt>
   0x080484be <+49>:    leave
   0x080484bf <+50>:    ret
End of assembler dump.
(gdb) c
Continuing.
a.x = 10
a.y = 20
ap->x = 10  
ap->y = 20
[Inferior 1 (process 106089) exited normally]
(gdb) q

실습> PAM 소스에서 구조체와 함수 포인터 적용 예

PAM에서 구조체에 함수 포인터를 이용해서 함수의 주소를 저장하는 부분

PAM 모듈은 구조체로 되어 있고 각 멤버 변수는 함수를 저장할 수 있는 함수 포인터로 구성되어 있다.
-- /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/libpam/include/security/pam_modules.h --

 37 struct pam_module {
 38     const char *name;       /* Name of the module */
 39
 40     /* These are function pointers to the module's key functions.  */
 41
 42     int (*pam_sm_authenticate) (pam_handle_t *pamh, int flags, int argc, const char **argv);
 43     int (*pam_sm_setcred)      (pam_handle_t *pamh, int flags, int argc, const char **argv);
 44     int (*pam_sm_acct_mgmt)    (pam_handle_t *pamh, int flags, int argc, const char **argv);
 45     int (*pam_sm_open_session) (pam_handle_t *pamh, int flags, int argc, const char **argv);
 46     int (*pam_sm_close_session)(pam_handle_t *pamh, int flags, int argc, const char **argv);
 47     int (*pam_sm_chauthtok)    (pam_handle_t *pamh, int flags, int argc, const char **argv);
 48 };
-- /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/libpam/include/security/pam_modules.h --

모듈의 소스를 보면 모듈이 어디서 지원하는지에 따라서 함수 포인터가 저장되어 있는 주소가 각각 다르게 되어 있다.
pam_permit.c 는 모든 type에서 적용이 가능하고 모든 부분에 함수의 주소가 저장되어 있다.
-- /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_permit/pam_permit.c --

124 struct pam_module _pam_permit_modstruct = {
125     "pam_permit",
126     pam_sm_authenticate,  <-- auth
127     pam_sm_setcred,       <-- auth
128     pam_sm_acct_mgmt,     <-- account
129     pam_sm_open_session,  <-- session
130     pam_sm_close_session, <-- session
131     pam_sm_chauthtok      <-- password 
132 };
-- /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_permit/pam_permit.c --


o pam_succeed_if.so
모듈은 모든 타입에서 사용 가능
모듈의 인자로 조건을 설정하며, 조건이 참일 경우 성공을 반환
ex) auth	sufficient		user = user1 use_uid quiet

pam_succeed_if.c

578 /* static module data */
579 #ifdef PAM_STATIC
580 struct pam_module _pam_succeed_if_modstruct = {
581     "pam_succeed_if",
582     pam_sm_authenticate,
583     pam_sm_setcred,
584     pam_sm_acct_mgmt,
585     pam_sm_open_session,
586     pam_sm_close_session,
587     pam_sm_chauthtok
588 };
589 #endif


o pam_wheel.so
모듈은 auth type에서만 사용 가능
모듈은 root로 접근하는 사용자가 wheel 그룹의 구성원인 경우에만 허용하기 위해 사용
일반적으로 su의 보안 설정을 위해 사용 됨
ex) su	auth	sufficient		pam_rootok.so
ex) su	auth	required		pam_wheel.so trust use_uid
ex) su	auth	required		pam_unix.so
ex) su	auth	required		pam_permit.so

pam_wheel.c 

266 #ifdef PAM_STATIC
267 
268 /* static module data */
269 
270 struct pam_module _pam_wheel_modstruct = {
271     "pam_wheel",
272     pam_sm_authenticate,
273     pam_sm_setcred,
274     pam_sm_acct_mgmt,
275     NULL,
276     NULL,
277     NULL
278 };
279 
280 #endif /* PAM_STATIC */


o pam_motd
모듈은 session type에서만 사용 가능
motd 파일의 내용을 출력
ex) session	optional	pam_motd.so	motd=/etc/motd

pam_motd.c

114 #ifdef PAM_STATIC
115      
116 /* static module data */
117 
118 struct pam_module _pam_motd_modstruct = {
119      "pam_motd",
120      NULL,
121      NULL,
122      NULL,
123      pam_sm_open_session,
124      pam_sm_close_session,
125      NULL,
126 };
127 
128 #endif
129 
130 /* end of module definition */


pam_time.so
모듈은 account type에서만 사용 가능
로그인할 위치 및 시간을 지정하여 시간에 따라 로그인을 제어
ex) account	required	pam_time.so
설정 파일 : /etc/security/time.conf

pam_time.c

667 #ifdef PAM_STATIC
668     
669 /* static module data */
670     
671 struct pam_module _pam_time_modstruct = {
672     "pam_time",
673     NULL,
674     NULL,
675     pam_sm_acct_mgmt,
676     NULL,
677     NULL,
678     NULL
679 };  
680 #endif


pam_cracklib.so
모듈은 password 타입에서만 사용 가능
사용자 암호 정책을 확인
입력받은 암호를 /etc/lib64/cracklib_dict에 있는 사전 정보와 비교
새로운 암호를 /etc/security/opasswd에 저장되어 있는 이 전 암호 목록과 비교
시스템 보안 차원에서 패스워드 글자 수를 제한할 수 있음


pam_cracklib.c 
	
861 #ifdef PAM_STATIC
862 /* static module data */
863 struct pam_module _pam_cracklib_modstruct = {
864      "pam_cracklib",
865      NULL,
866      NULL,
867      NULL,
868      NULL,
869      NULL,
870      pam_sm_chauthtok
871 };
872 #endif


pam_limits.so
모듈은 session type에서만 사용 가능
사용자가 사용할 수 있는 로그인 세션의 자원을 설정 파일에 따라 제한
사용자에 가용 자원을 제한함으로써 자원에 영향을 주는 DoS 공격에 대비가 가능하다는 이점이 있음
ex) session	required	pam_limits.so
설정 파일 : /etc/security/limits.conf

pam_limits.c  

1095 #ifdef PAM_STATIC
1096 
1097 /* static module data */
1098 
1099 struct pam_module _pam_limits_modstruct = {
1100      "pam_limits",
1101      NULL,
1102      NULL,
1103      NULL,
1104      pam_sm_open_session,
1105      pam_sm_close_session,
1106      NULL
1107 };
1108 #endif

# yum -y reinstall pam
# useradd user1
# useradd user2
# useradd user3
# su - user1

[user1@localhost ~]$ id
uid=1001(user1) gid=1001(user1) groups=1001(user1)  ... (생략)

-- /etc/pam.d/su --
  3 auth        sufficient  pam_rootok.so debug   <-- 실패
  4 auth        sufficient  pam_wheel.so trust use_uid debug  <-- 실패
  5 auth        substack    system-auth   <-- 성공
  :
  :(생략)
-- /etc/pam.d/su --

[user1@localhost ~]$ id
uid=1001(user1) gid=1001(user1) groups=1001(user1) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[user1@localhost ~]$ su -
암호:  <-- root 비번이 맞으면 root로 변경된다.
마지막 로그인: 수  3월 29 09:17:50 KST 2023 200.200.200.1에서 시작 일시 pts/2
[root@localhost ~]# exit
[user1@localhost ~]$

# usermod -G wheel user1
# grep wheel /etc/group
wheel:x:10:user1
# su - user1
[user1@localhost ~]$ id
uid=1001(user1) gid=1001(user1) groups=1001(user1),10(wheel) ...(생략)

[user1@localhost ~]$ su -
마지막 로그인: 수  3월 29 16:20:08 KST 2023 일시 pts/2
[root@localhost ~]# exit

-- /etc/pam.d/su --
  3 auth        sufficient  pam_rootok.so debug
  4 #auth       sufficient  pam_wheel.so trust use_uid debug
  5 auth        required    pam_wheel.so use_uid  <-- wheel 그룹에 포함된 사용자만 su 명령어를 사용할 수 있다.
  6 auth        substack    system-auth
-- /etc/pam.d/su --

[user1@localhost ~]$ su -
암호:  <-- root의 비밀번호를 입력한다.
마지막 로그인: 수  3월 29 16:21:47 KST 2023 일시 pts/2
[root@localhost ~]# exit
[user1@localhost ~]$ exit

# su - user2
마지막 로그인 실패: 토  3월 25 17:12:37 KST 2023 일시 pts/4
마지막 로그인 후 3 번의 로그인 시도가 실패하였습니다.
[user2@localhost ~]$ id
uid=1002(user2) gid=1002(user2) groups=1002(user2) ... (생략)
[user2@localhost ~]$ su -
암호:  <-- root의 비밀번호를 정확히 입력해도 root의 쉘을 얻을 수 없다. (wheel 그룹에 포함되지 않았기 때문이다.)
su: 권한 부여 거부

실습> ssh motd

motd: Message Of The Day

[root@localhost ~]# grep -i motd /etc/ssh/sshd_config
#PrintMotd yes

[root@localhost ~]# cat >> /etc/motd << EOF
안녕하세요.
반갑습니다.
EOF

[root@localhost ~]# ssh root@localhost
root@localhost's password:
Last login: Wed Mar 29 17:05:23 2023 from localhost
안녕하세요.
반갑습니다.

[root@localhost ~]# exit
[root@localhost ~]# rm -f /etc/motd

실습> pam_motd.so

motd: Message Of The Day

/etc/motd 파일은 sshd 데몬에서 기본 설정으로 되어 있으므로 여기서는 파일명을 /etc/motd2로 변경해서 설정한다.

1. PAM 파일 수정
-- /etc/pam.d/sshd --
 12 session    optional     pam_motd.so    motd=/etc/motd2
 13 session    required     pam_selinux.so close
-- /etc/pam.d/sshd --

2. 메세지 파일 생성
/etc/motd는 ssh로 접속하면 기본적으로 보여지는 파일이므로 여기서는 /etc/motd2로 파일을 생성한다.
# vi /etc/motd2
>>> 공지사항 <<< 
시스템 점검이 2023.4.1 ~ 4.2 01:00 ~ 07:00 까지 새벽 작업이 있을 예정입니다.
서버에 접속은 할 수 없음을 알려드립니다.

점검일시: 2023.4.1 ~ 4.2 01:00 ~ 07:00 
담당자: 인프라 시스템 홍길동 팀장
연락처: 010-1111-2222

문의 사항은 연락처로 연락주시기 바랍니다.
감사합니다.

3. 사용자 생성
[root@localhost ~]# useradd user1
[root@localhost ~]# passwd user1

4. 서버 접속
cmd에서 ssh로 서버에 접속하면 /etc/motd2 파일의 내용이 출력된다.
C:\Users\boani>ssh user1@200.200.200.3
user1@200.200.200.3's password:
>>> 공지사항 <<<
시스템 점검이 2023.4.1 ~ 4.2 01:00 ~ 07:00 까지 새벽 작업이 있을 예정입니다.
서버에 접속은 할 수 없음을 알려드립니다.

점검일시: 2023.4.1 ~ 4.2 01:00 ~ 07:00
담당자: 인프라 시스템 홍길동 팀장
연락처: 010-1111-2222

문의 사항은 연락처로 연락주시기 바랍니다.
감사합니다.
Last login: Wed Mar 29 16:21:19 2023
[user1@localhost ~]$


# cd /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_motd
# vi pam_motd.c 
 51 PAM_EXTERN
 52 int pam_sm_open_session(pam_handle_t *pamh, int flags,
 53             int argc, const char **argv)
 54 {
 55     printf("pam_sm_open_session() 실행!\n");
  :
  :(생략)
 82     printf("motd_path: %s\n", motd_path);
 83                                                                                             
 84     if (motd_path == NULL)
 85     motd_path = default_motd;

# make
# setenforce 0
# mv .libs/pam_motd.so /usr/lib64/security/
mv: overwrite `/usr/lib64/security/pam_motd.so'? y

profile
정보보안 전문가

0개의 댓글