실습> 함수 포인터
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