시스템 보안 운영 - 2 (교육 83일차)

SW·2023년 3월 23일
0

실습> 컴파일 과정 알아보기

참고 : https://cafe.naver.com/linuxmasternet/122

사람                                                            컴퓨터
       전처리기(gcc)     컴파일러(cc1)  어셈블러(as)   링커(ld)
          gcc -E         gcc -S         gcc -c          gcc -o   
myprogram.c  -> myprogram.i -> myprogram.s -> myprogram.o -> myprogram
 소스파일       전처리파일    어셈블리파일     목적 파일     실행파일
          --------------------------------------------------------->
	                       컴파일 과정 (개발자)                       
                                   <--------------------------------
                                         디스어셈블(정보보안 전문가, 리버서)   
          <---------------------------------------------------------
	                      디컴파일 과정(정보보안 전문가, 리버서)   

리버싱은 실행파일을 분석하는 과정으로 주로 악성코드 분석 분야에 필수로 사용된다.

# wget --no-check-certificate http://linuxmaster.net/tools/gccInstall.sh
# chmod 755 gccInstall.sh
# ./gccInstall.sh
# yum -y install vim


cat << EOF >> /etc/vimrc

set nu
set ai
set ci
set bg=dark
set cursorline
set expandtab
set ts=4
EOF

alias vi=vim
cat << EOF >> ~/.bashrc
alias vi=vim
EOF


# cat << EOF > myprogram.c
/*
 * 파일명: myprogram.c
 * 프로그램 설명: 컴파일 과정 분석하기
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>
#define TODAY "Linux Security"

int main()
{
    printf("%s: 컴파일 과정 분석하기\n", TODAY);
    return 0;
}
EOF

전처리기에 의해서 소스 파일을 전처리 파일로 생성한다.
# gcc -E myprogram.c -o myprogram.i
# cat myprogram.i

전처리 파일을 어셈블러가 어셈블리 파일로 생성한다.
# gcc -S myprogram.i -o myprogram.s  <-- 64bit at&t 문법
# gcc -masm=intel -S myprogram.i -o myprogram.s  <-- 64bit intel 문법

# gcc -m32 -S myprogram.i -o myprogram.s  <-- 32bit at&t 문법
# gcc -m32 -masm=intel -S myprogram.i -o myprogram.s  <-- 32bit intel 문법


어셈블리 파일을 오브젝트 파일로 생성한다.
# gcc -c myprogram.s  <-- 64bit
# gcc -m32 -c myprogram.s  <-- 32bit

오브젝트 파일을 링커가 printf.o와 결합시켜서 최종 실행파일로 생성한다.
myprogram.o + pintf.o

동적 컴파일 (Default)
# gcc myprogram.o -o myprogramDynamic  <-- 64bit
# gcc -m32 myprogram.o -o myprogramDynamic  <-- 32bit
# ls -l myprogramDynamic
-rwxr-xr-x. 1 root root   8360  3월  4 08:24 myprogramDynamic
# ./myprogramDynamic
Linux Security: 컴파일 과정 분석하기
# ldd myprogramDynamic
    linux-vdso.so.1 =>  (0x00007ffd16978000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f51836eb000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f5183ab9000)

정적 컴파일
정적 컴파일을 하기 위해서는 -static 옵션을 사용한다.
64bit로 정적 컴파일을 하기 위해서는 glibc-static 패키지가 필요하다.
32bit로 정적 컴파일을 하기 위해서는 glibc-static.686 패키지가 필요하다. 
# gcc -static myprogram.o -o myprogramStatic  <-- 64bit
# gcc -m32 -static myprogram.o -o myprogramStatic  <-- 32bit
# ls -l myprogramStatic 
-rwxr-xr-x. 1 root root 861216  3월  4 08:23 myprogramStatic
# ./myprogramStatic
Linux Security: 컴파일 과정 분석하기
# ldd myprogramStatic
    동적 실행 파일이 아닙니다

동적 컴파일 VS 정적 컴파일
- 파일의 크기를 비교해보면 정적으로 컴파일하면 실행파일 안에 printf.o가 들어있으므로 파일이 용량이 크다.
# ll myprogramDynamic myprogramStatic 
-rwxr-xr-x. 1 root root   7220  3월 22 17:29 myprogramDynamic
-rwxr-xr-x. 1 root root 773980  3월 22 17:33 myprogramStatic

32bit C 라이브러리에서 printf.o를 확인(t)한다.
# ar t /usr/lib/libc.a printf.o
printf.o

32bit C 라이브러리에서 printf.o를 삭제(d)한다.
# ar d /usr/lib/libc.a printf.o 

32bit C 라이브러리에서 printf.o를 확인(t)하면 삭제했으므로 에러가 발생된다.
# ar t /usr/lib/libc.a printf.o
no entry printf.o in archive

실행파일 myprogramStatic 를 뽑아내기 위해서는 myprogram.o + printf.o 가 필요한데
printf.o 가 없기 때문에 에러가 발생되서 최종적으로 실행파일을 뽑을 수가 없다.
# gcc -m32 -static myprogram.o -o myprogramStatic 
myprogram.o: In function `main':
myprogram.c:(.text+0x19): undefined reference to `printf'
collect2: error: ld returned 1 exit status

32bit C 라이브러리를 재설치하고 다시 printf.o를 확인한다.
# yum -y reinstall glibc-static.i686
# ar t /usr/lib/libc.a printf.o
printf.o  <-- 정상적으로 패키지가 설치되었기 때문에 printf.o 가 들어있다.

32bit C 라이브러리에서 printf.o Object 파일을 추출한다.
형식: ar x 정적라이브러명 오브젝트명
# gcc -m32 -static myprogram.o -o myprogramStatic 
# ar x /usr/lib/libc.a printf.o 
# ls
anaconda-ks.cfg  libc.a       myprogram.o       myprogramStatic
file.tar.gz      myprogram.c  myprogram.s       printf.o  <-- 추출된 printf.o 
gccInstall.sh    myprogram.i  myprogramDynamic

32bit C 라이브러리에서 printf.o Object 파일을 삭제한다.
# ar d /usr/lib/libc.a printf.o 
# ar t /usr/lib/libc.a printf.o 
no entry printf.o in archive

/usr/lib/libc.a 라이브러리의 printf.o 를 참고하는데 지웠기 때문에 에러가 발생된 것이다.
# gcc -m32 -static myprogram.o -o myprogramStatic2 
myprogram.o: In function `main':
myprogram.c:(.text+0x19): undefined reference to `printf'
collect2: error: ld returned 1 exit status

현재 디렉터리에 추출한 printf.o를 같이 컴파일을 하면 실행파일을 생성할 수 있다.
# gcc -m32 -static myprogram.o printf.o -o myprogramStatic2 
# ll myprogramStatic2
-rwxr-xr-x. 1 root root 773980  3월 22 19:55 myprogramStatic2

# file myprogramStatic2
myprogramStatic2: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=918cf21de02d39133d69dc22843777e61cc3c5dc, not stripped

# ./myprogramStatic2
Linux Security: 컴파일 과정 분석하기

실습> 함수에 대한 이해

함수란 1개 이상의 실행문을 하나로 묶은 것이며 프로그램에서 재사용 하기 위해서 만든다.
함수는 코드의 중복을 제거하고 다시 사용하는 재사용성의 의미를 가지고 있다.

함수 종류
라이브러리 함수: 파이썬을 설치하면 제공되는 함수 (print, input)
패키지 함수: 추가적인 패키지를 설치하면 제공되는 함수
사용자 정의 함수: 사용자가 직접 만들어서 사용하는 함수


입력값은 함수를 호출할 때 함수에 전달하는 값을 말한다.
입력값 : 인수, 인자, 아규먼트, 파라미터, 매개변수

출력값은 함수가 종료하면 호출된 쪽으로 값을 전달하는 것을 말한다.
출력값 : 리턴값

인수, 인자, argument :
- 함수로 전달되는 값을 말한다.
- 함수 호출 부분에 있다. e.g.) max(1,2), printf("Hello")

매개변수, parameter : 
- 함수 내부에서 함수로 전달된 값을 저장한 변수를 말한다. 
- 함수 정의 부분에 있다.
e.g.)
int max(i, j)
{
    :
    :
}

함수의 종료 시점
- 함수안에 실행문들이 끝난 경우
- 함수에서 리턴문을 만난 경우

함수는 선언, 정의, 호출이 있고 함수 선언, 함수 정의, 함수 호출이 나중에 나온다.
함수 호출이 먼저 나오고 함수 정의가 뒤에 오면 에러가 발생된다.


1. 함수 선언
컴파일러에게 함수가 있다는걸 알려준다.
#include <stdio.h>
extern int printf (const char *__restrict __format, ...);

2. 함수 정의 
함수를 만든다.
리턴값 함수명(매개변수)
{
    실행문
}

리턴값: void, int, float, char *, struct student ...

함수명: myfunction
매개변수: 없음
리턴값: 없음
함수 기능: 환영 메세지 출력

void myfunction()
{
    printf("Hello function!\n");
}

3. 함수 호출 
만들어진 함수를 실행한다.
myfunction();   


-- exFunction1.c --
/*
 * 파일명: exFunction1.c
 * 프로그램 설명: 함수 
 * 작성자: 리눅스마스터넷
 *
 * 32bit 동적 컴파일: gcc -Wall -m32 소스파일 -o 실행파일
 * 32bit 정적 컴파일: gcc -Wall -static -m32 소스파일 -o 실행파일
 * 64bit 동적 컴파일: gcc -Wall 소스파일 -o 실행파일
 * 64bit 정적 컴파일: gcc -Wall -static 소스파일 -o 실행파일
 */

#include <stdio.h>

void myfunction();  // 함수 선언

int main()
{
    myfunction();  // 함수 호출
    return 0;
}

void myfunction()  // 함수 정의
{
    printf("Hello function!\n");
}
-- exFunction1.c --

32bit 동적 컴파일(동적 링킹)을 이용해서 exFunction1-1 실행파일을 생성한다.
# gcc -Wall -m32 exFunction1.c -o exFunction1-1
# ldd exFunction1-1
        linux-gate.so.1 =>  (0xf777b000)
        libc.so.6 => /lib/libc.so.6 (0xf75a9000)  <-- 메모리 주소가 32bit(4byte)
        /lib/ld-linux.so.2 (0xf777c000)
# ./exFunction1-1
Hello function!

32bit 정적 컴파일을 이용해서 exFunction1-2 실행파일을 생성한다.
# gcc -Wall -static -m32 exFunction1.c -o exFunction1-2
# ldd exFunction1-2
        동적 실행 파일이 아닙니다
# ./exFunction1-2
Hello function!

64bit 동적 컴파일(동적 링킹)을 이용해서 exFunction1-3 실행파일을 생성한다.
# gcc -Wall exFunction1.c -o exFunction1-3
# ldd exFunction1-3
        linux-vdso.so.1 =>  (0x00007ffe7e941000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fc809f94000)  <-- 메모리 주소가 64bit(8byte)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc80a362000)
# ./exFunction1-3
Hello function!

64bit 정적 컴파일을 이용해서 exFunction1-4 실행파일을 생성한다.
# gcc -Wall -static exFunction1.c -o exFunction1-4
# ldd exFunction1-4
        동적 실행 파일이 아닙니다
# ./exFunction1-4
Hello function!

# file exFunction1-[1-4]
exFunction1-1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=56bf5d942dcf701013c2234cfd7f2e4e073ed625, not stripped
exFunction1-2: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=1a81cb2d0f2cdf22c22ca3228c8a35de0b88aed2, not stripped
exFunction1-3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c079d09294ce1ec362e0aa3fef719415ba19e972, not stripped
exFunction1-4: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=1fcb1963d061263c8cfc9cbef1eb86aa9da31a43, not stripped

-- exFunction2.c --
/*
 * 파일명: exFunction2.c
 * 프로그램 설명: 함수 
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

/*
 * 함수를 사용한 경우
 * function 함수 정의
 * 함수명 : function
 * 매개변수 : 없음
 * 리턴값 : 없음
 * 함수 기능 : i 변수에 1을 저장하고 출력한다.
 */

void function();  // 함수 선언

int main()
{
    function();  // 함수 호출
    return 0;
}

// 함수 정의
void function()
{
    int i = 1;
    printf("%d\n", i);
}
-- exFunction2.c --

# gcc -Wall  exFunction2.c -o exFunction2
# ./exFunction2
1


-- exFunction3.c --
/*
 * 파일명: exFunction3.c
 * 프로그램 설명: 4가지 유형의 첫 번째 함수 만들기 예제 
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int f1(int x);

int main()
{
    int number;
    number = f1(3);
    printf("%d\n", number);

    return 0;
}

/*
 * 첫 번째 유형의 함수
 * 첫 번째 유형의 함수명 : f1
 * 매개변수 : 1개 있음
 * - 매개변수명 : x
 * 리턴값   : 있음
 * - 리턴값 : y
 * 기능 : 매개변수에 5를 더해서 호출한 쪽으로 돌려준다.
 */
int f1(int x)
{
    int y;
    y = x + 5;
    return y;
}
-- exFunction3.c --

# gcc -Wall -o exFunction3  exFunction3.c
# ./exFunction3
8

-- exFunction4.c --
/*
 * 파일명: exFunction4.c
 * 프로그램 설명: 4가지 유형의 두 번째 함수 만들기 예제 
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

void f2();

int main()
{
    f2();
    return 0;
}

/*
 * 두 번째 유형의 함수
 * 두 번째 유형의 함수명 : f2
 * 매개변수 : 없음
 * 리턴값   : 없음
 * 기능 : 화면에 8을 출력한다.
 */
void f2()
{
    printf("8\n");
}
-- exFunction4.c --

# gcc -Wall exFunction4.c -o exFunction4
# ./exFunction4
8

-- exFunction5.c --
/*
 * 파일명: exFunction5.c
 * 프로그램 설명: 4가지 유형의 세 번째 함수 만들기 예제 
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

void f3(int x);

int main()
{
    f3(3);   // 8
    f3(10);  // 15

    return 0;
}

/*
 * 세 번째 유형의 함수
 * 세 번째 유형의 함수명 : f3
 * 매개변수 : 1개 있음
 * - 매개변수명 : x
 * 리턴값   : 없음
 * 기능 : 매개변수에 5를 더해서 화면에 y를 출력한다.
 */
void f3(int x)
{
    int y;
    y = x + 5;
    printf("%d\n", y);
}
-- exFunction5.c --

# gcc -Wall -o exFunction5 exFunction5.c
# ./exFunction5
8

-- exFunction6.c --
/*
 * 파일명: exFunction6.c
 * 프로그램 설명: 4가지 유형의 네 번째 함수 만들기 예제 
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int f4();

int main()
{
    int retValue;

    retValue = f4();
    printf("%d\n", retValue);  // 8
    printf("%d\n", f4());      // 8

    return 0;
}

/*
 * 네 번째 유형의 함수
 * 네 번째 유형의 함수명 : f4
 * 매개변수 : 없음
 * 리턴값   : 있음
 * - 리턴값 : y
 * 기능 : y에 8을 저장한 후 y를 호출한 쪽으로 돌려준다.
 */
int f4()
{
    int y;
    y = 3 + 5;
    return y;
}
-- exFunction6.c --

# gcc -Wall exFunction6.c -o exFunction6
# ./exFunction6
8
8

실습> 정적 라이브러리 만들기

# mkdir library1_static
# cd library1_static

# vi a.c
#include <stdio.h>

void a()
{
    printf("a() \n");
}


# vi main.c
#include <stdio.h>

void a();

int main()
{
    a();
    printf("main() \n");

    return 0;
}

# gcc -Wall -c a.c
# gcc -Wall -c main.c
# gcc -o main main.o a.o
# ./main
a()
main()

# ll
합계 16
-rw-r--r--. 1 root root   56  3월 22 22:05 a.c
-rw-r--r--. 1 root root 1480  3월 22 22:10 a.o
-rw-r--r--. 1 root root   96  3월 22 22:08 main.c
-rw-r--r--. 1 root root 1552  3월 22 22:10 main.o


# vi b.c
#include <stdio.h>

void b()
{
    printf("b()\n");
}

# vi c.c
#include <stdio.h>

void c()
{
    printf("c()\n");
}


# vi main2.c
#include <stdio.h>

void a();
void b();
void c();

int main()
{
    a();
    b();
    c();
    printf("main() \n");

    return 0;
}


# gcc -c b.c c.c main2.c

Object 파일을 생성해서 main2.o 파일과 결합해서 실행파일 main2-1을 생성한다.
- main2.o + a.o + b.o = c.o
# gcc -o main2-1 main2.o a.o b.o c.o
# ./main2-1
a()
b()
c()

Object 파일을 라이브러리 libmyfunction.a 로 생성한다.
# ar rcs libmyfunction.a a.o b.o c.o
# ar t libmyfunction.a
a.o
b.o
c.o

Object 파일을 main2.c 파일과 결합해서 실행파일 main2-2를 생성한다.
- libmyfunction.a (a.o, b.o, c.o)
# gcc -o main2-2 main2.c libmyfunction.a
# ./main2-2
a()
b()
c()
main()

컴파일 에러
- a.o, b.o, c.o 가 없기 때문이다.
# gcc -o main2-3 main2.c  

컴파일 에러
- 라이브러리 디렉터리를 지정해주지 않았기 때문이다.
# gcc -o main2-3 main2.c -lmyfunction

동적으로 컴파일한다.
-L. 라이브러리가 현재 디렉터리에 있으므로 이 디렉터리를 반드시 명시해야 한다.
- -L 옵션이 없으면 /usr/lib, /usr/lib64 디렉터리에서 라이브러리를 찾는다.
# gcc -o main2-3 main2.c -lmyfunction -L.
# ./main2-3
a()
b()
c()
main()

정적으로 컴파일한다.
# gcc -static -o main2-4 main2.c -lmyfunction -L.

# ldd main2-3
        linux-vdso.so.1 =>  (0x00007ffe39e78000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fc73ecbb000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc73f089000)

# ldd main2-4
        동적 실행 파일이 아닙니다

# ./main2-3
a()
b()
c()
main()

# ./main2-4
a()
b()
c()
main()

실습> 정적 라이브러리 만들기

정적 라이브러리명: libmylibrary.a
오브젝트: f1.o, f2.o f3.o f4.o

1. 소스코드 생성
-- f1.c --
/*
 * 파일명: f1.c
 * 프로그램 설명: 4가지 유형의 첫 번째 함수 만들기 예제 
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

/*
 * 첫 번째 유형의 함수
 * 첫 번째 유형의 함수명 : f1
 * 매개변수 : 1개 있음
 * - 매개변수명 : x
 * 리턴값   : 있음
 * - 리턴값 : y
 * 기능 : 매개변수에 5를 더해서 호출한 쪽으로 돌려준다.
 */
int f1(int x)
{
    int y;
    y = x + 5;
    return y;
}
-- f1.c --

-- f2.c --
/*
 * 파일명: f2.c
 * 프로그램 설명: 4가지 유형의 두 번째 함수 만들기 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

/*
 * 두 번째 유형의 함수
 * 두 번째 유형의 함수명 : f2
 * 매개변수 : 없음
 * 리턴값   : 없음
 * - 리턴값 : None을 리턴한다.
 * 기능 : 화면에 8을 출력한다.
 */
void f2()
{
    printf("8\n");
}
-- f2.c --

-- f3.c --
/*
 * 파일명: f3.c
 * 프로그램 설명: 4가지 유형의 세 번째 함수 만들기 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

/*
 * 세 번째 유형의 함수
 * 세 번째 유형의 함수명 : f3
 * 매개변수 : 1개 있음
 * - 매개변수명 : x
 * 리턴값   : 없음
 * 기능 : 매개변수에 5를 더해서 화면에 y를 출력한다.
 */
void f3(int x)
{
    int y;
    y = x + 5;
    printf("%d\n", y);
}
-- f3.c --

-- f4.c --
/*
 * 파일명: f4.c
 * 프로그램 설명: 4가지 유형의 네 번째 함수 만들기 예제
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

/*
 * 네 번째 유형의 함수
 * 네 번째 유형의 함수명 : f4
 * 매개변수 : 없음
 * 리턴값   : 있음
 * - 리턴값 : y
 * 기능 : y에 8을 저장한 후 y를 호출한 쪽으로 돌려준다.
 */
int f4()
{
    int y;
    y = 3 + 5;
    return y;
}
-- f4.c --

2. 헤더파일 생성
# vi myheader.h
/*
 * 파일명: myheader.h
 * 프로그램 설명: 함수 선언용 헤더파일
 * 작성자: 리눅스마스터넷
 */
int f1(int x);
void f2();
void f3(int x);
int f4();

3. main 소스 생성
# vi functionTest.c
/*
 * 파일명: functionTest.c
 * 프로그램 설명: 라이브러리와 컴파일 테스트
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>
#include "myheader.h"  // 함수 선언
/*
 * int f1(int x);
 * void f2();
 * void f3(int x);
 * int f4();
 */

int main()
{
    int a, b;

    a = f1(3);
    f2();
    f3(3);
    b = f4();

    printf("a = %d, b = %d\n", a, b);

    return 0;
}

4. 라이브러리 생성
64bit 용 정적 라이브러리 libmylibrary.a 를 생성한다.
# gcc -Wall -c f1.c f2.c f3.c f4.c
# ar rcs libksw.a f1.o f2.o f3.o f4.o
# ar t libksw.a
f1.o
f2.o
f3.o
f4.o

5. 컴파일 및 실행
libksw.a 파일을 이용해서 동적으로 컴파일(동적 링킹)한다.
# gcc -o functionTest1 functionTest.c libksw.a
# ll functionTest
-rwxr-xr-x. 1 root root   8640  3월 23 00:42 functionTest
# ldd functionTest1 
functionTest1:
        linux-vdso.so.1 =>  (0x00007fff97db9000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fad4b242000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fad4b610000)

# ./functionTest1
8
8
a = 8, b = 8

libksw.a 파일을 이용해서 정적으로 컴파일(정적 링킹)한다.
# gcc -static -o functionTest2 functionTest.c -lksw -L.
# ll functionTest2
-rwxr-xr-x. 1 root root 861616  3월 23 00:42 functionTest2

# ldd functionTest2
functionTest2:
        동적 실행 파일이 아닙니다

# ./functionTest2
8
8
a = 8, b = 8

실습> 공유 라이브러리

공유 라이브러리란?
프로그램이 시작할 때 적재되는 라이브러리로써 정적 라이브러리의 단점을 보완해서 나온 라이브러리이다.

참고:
https://en.wikipedia.org/wiki/Position-independent_code


공유 라이브러리명
- library's realname  : libmyfunction.so.1.0.0
- library's soname    : libmyfunction.so.1
- library's linkername: libname.so

오브젝트: a.o, b.o, c.o


1. 소스코드 작성

# mkdir library2_shared; cd library2_shared

-- a.c --
/*
 * 파일명: a.c
 * 프로그램 설명: 공유 라이브러리와 컴파일 테스트
 * 작성자: 리눅스마스터넷
 */
#include <stdio.h>

void a()
{
    printf("a() \n");
}
-- a.c --

-- b.c --
/*
 * 파일명: b.c
 * 프로그램 설명: 공유 라이브러리와 컴파일 테스트
 * 작성자: 리눅스마스터넷
 */
#include <stdio.h>

void b()
{
    printf("b()\n");
}
-- b.c --

-- c.c -- 
/*
 * 파일명: c.c
 * 프로그램 설명: 공유 라이브러리와 컴파일 테스트
 * 작성자: 리눅스마스터넷
 */
#include <stdio.h>

void c()
{
    printf("c()\n");
}
-- c.c -- 

-- main.c --
/*
 * 파일명: main.c
 * 프로그램 설명: 공유 라이브러리와 컴파일 테스트
 * 작성자: 리눅스마스터넷
 */
#include <stdio.h>

void a();
void b();
void c();

int main()
{
    a();
    b();
    c();
    printf("main() \n");

    return 0;
}
-- main.c --


2. 공유 라이브러리로 컴파일 하기
-fPIC 옵션을 줘야하고 PCI는(Postion Independent Code)의 약자이고 이 옵션을 주고 컴파일 한다.
참고로 공유 라이브러리는 -fPIC 옵션이 없으면 에러가 발생되므로 반드시 -fPIC 옵션을 사용한다.
library's realname  : libmyfunction.so.1.0.0
library's soname    : libmyfunction.so.1
library's linkername: libname.so
# gcc -fPIC -c a.c b.c c.c
# gcc -shared -Wl,-soname,libmyfunction.so.1 -o libmyfunction.so.1.0.0 a.o b.o c.o
# ln -s libmyfunction.so.1.0.0 libmyfunction.so.1
# ln -s libmyfunction.so.1.0.0 libmyfunction.so

# ll
합계 36
-rw-r--r--. 1 root root   56  3월 23 01:05 a.c
-rw-r--r--. 1 root root 1528  3월 23 01:18 a.o
-rw-r--r--. 1 root root   55  3월 23 01:05 b.c
-rw-r--r--. 1 root root 1528  3월 23 01:18 b.o
-rw-r--r--. 1 root root   55  3월 23 01:05 c.c
-rw-r--r--. 1 root root 1520  3월 23 01:18 c.o
lrwxrwxrwx. 1 root root   22  3월 23 01:19 libmyfunction.so -> libmyfunction.so.1.0.0
lrwxrwxrwx. 1 root root   22  3월 23 01:19 libmyfunction.so.1 -> libmyfunction.so.1.0.0
-rwxr-xr-x. 1 root root 8128  3월 23 01:18 libmyfunction.so.1.0.0
-rw-r--r--. 1 root root  134  3월 23 01:08 main.c


# gcc -o main main.c -lmyfunction -L.
# ldd main
        linux-vdso.so.1 =>  (0x00007ffce73f8000)
        libmyfunction.so.1 => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f5a28053000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f5a28421000)

공유라이브러리를 등록하지 않으면 현재 디렉터리의 공유라이브러를 인식할 수 없으므로 
./main을 실행하면 라이브러리 에러가 발생한다.
# ./main
./main: error while loading shared libraries: libmyfunction.so.1: cannot open shared object file: No such file or directory


공유라이브러리를 이용해서 컴파일 한 실행파일은 반드시 공유 라이브러의 위치를 알려줘야 한다. 
3가지 방법으로 라이브러리를 인식시켜야 한다.
첫 번째: 시스템의 라이브러리 디렉토리에 공유 라이브러가 존재하면 인식된다. 
- root 사용 가능, 일반 유저 사용 불가능 (허가권에 w가 없다.)
- /usr/lib, /usr/lib64
dr-xr-xr-x. 32 root root  8192  3월 22 23:49 /usr/lib
dr-xr-xr-x. 38 root root 20480  3월 23 01:38 /usr/lib64
파일: rw 권한은 root는 제약을 받지 않는다. 
----------. 1 root root /etc/shadow  <-- 일반 파일에 읽기와 쓰기 권한이 없어도 root는 읽고 쓸 수 있음.

파일: x 권한 제약을 받는다. 
----------. 1 root root /bin/mv  <-- 실행파일에 실행권한이 없으면 root라도 실행할 수 없음.

디렉터리: rwx 권한은 root는 제약을 받지 않는다. 
d---------. 5 root root /root <-- 디렉터리에 rwx 권한이 없어도 root는 읽고, 쓰고, 실행할  있음


>>> 첫 번째 실습 <<<
# mv libmyfunction.so* /usr/lib64/
# ldd main
        linux-vdso.so.1 =>  (0x00007ffdd3b95000)
        libmyfunction.so.1 => /lib64/libmyfunction.so.1 (0x00007fb6e861a000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fb6e824c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb6e881c000)
# ./main
a()
b()
c()
main()

두 번째: 시스템의 라이브러리 디렉터리 이외에 저장하고자 한다면 /etc/ld.so.conf 파일에 공유 라이브러리 디렉토리 
경로를 추가하고 ldconfig 명령어를 실행해야 한다.
- root 사용 가능, 일반 유저 사용 불가능 (허가권에 w가 없다.)
-rw-r--r--. 1 root root 28  2월 28  2013 /etc/ld.so.conf

>>> 두 번째 실습 <<<
# pwd
/root/library2_shared
# mv /usr/lib64/libmyfunction.so* .
# ldd main
        linux-vdso.so.1 =>  (0x00007ffe42df2000)
        libmyfunction.so.1 => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f2ac4b2e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2ac4efc000)

# vi /etc/ld.so.conf.d/myfunction-x86_64.conf
/root/library2_shared

# file /etc/ld.so.cache
/etc/ld.so.cache: data
# strings /etc/ld.so.cache | grep library2_shared
  <-- /root/library2_shared 가 없다.

ldconfig 명령어를 실행하면 /etc/ld.so.conf.d/myfunction-x86_64.conf 파일의 내용을 읽어서 /etc/ld.so.cache 에 저장한다.
# ldconfig
# strings /etc/ld.so.cache | grep library2_shared
/root/library2_shared/libmyfunction.so.1  <-- /root/library2_shared 
/root/library2_shared/libmyfunction.so    <-- /root/library2_shared 
# ldd main
        linux-vdso.so.1 =>  (0x00007ffd25936000)
        libmyfunction.so.1 => /root/library2_shared/libmyfunction.so.1 (0x00007f9222455000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f9222087000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9222657000)
# ./main
a()
b()
c()
main()

세 번째: LD_LIBRARY_PATH 환경변수에 공유 라이브러리의 디렉토리를 설정한다. 
변수를 설정하고 export 명령어를 실행한다. 
ld_library_path 취약점을 구글에서 검색하면 많은 정보가 나온다.
- root 사용 가능, 일반 유저 사용 가능

>>> 세 번째 실습 <<<
# rm -f /etc/ld.so.conf.d/myfunction-x86_64.conf
# ldconfig
# ldd main
        linux-vdso.so.1 =>  (0x00007ffc27cc9000)
        libmyfunction.so.1 => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f8f18863000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8f18c31000)

/usr/lib, /usr/lib64 디렉터리 이외의 디렉터리에 라이브러리가 있으면 환경변수 LD_LIBRARY_PATH에 디렉터리를 등록하고
실행파일을 실행한다.
# export LD_LIBRARY_PATH=.
# echo $LD_LIBRARY_PATH
.
# ldd main
        linux-vdso.so.1 =>  (0x00007ffe611fa000)
        libmyfunction.so.1 => ./libmyfunction.so.1 (0x00007f1a8f540000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f1a8f172000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1a8f742000)

# ./main
a()
b()
c()
main()

실습> 동적 라이브러리

동적 적재 라이브러리란?
동적 라이브러리란 동적 적재 라이브러리를 줄여서 쓴 말로써 프로그램이 시작(run time)될 때가 아닌 필요할 때 
적재시키는 라이브러리이다. 

또한 소스 파일에 반드시 dlfcn.h 헤더파일을 반드시 포함해야 하며 컴파일시에 -ldl 옵션을 이용해 컴파일해야 한다.

>>> 동적 라이브러리 예 <<
# ls /usr/lib64/xtables/
/usr/lib64/xtables/libxt_mac.so
# lsmod | grep -i mac

# iptables -A INPUT -m mac --mac-source 11:22:33:44:55:66 -j ACCEPT
# lsmod | grep -i mac
xt_mac                 12492  1  <-- 메모리에 올라간 걸 확인한 모습이다.

# iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            MAC 11:22:33:44:55:66


# cd 
# mkdir library3_dynamic; cd library3_dynamic

-- plus.c --
int plus(int i)
{
    return i + 1;
}
-- plus.c --

-- minus.c --
int minus(int i)
{
    return i - 1;
}
-- minus.c --

-- main.c --
/*
 * 파일명: main.c
 * 프로그램 설명: 동적 적재 라이브러리와 컴파일 테스트
 * 작성자: 리눅스마스터넷
 */
#include <dlfcn.h>
#include <stdio.h>

int main()
{
    void *lib;         // dlopen handle 선언
    int (*plus)(int);  // 함수포인터 plus 선언
    int (*minus)(int); // 함수포인터 minus 선언
    int result1;       // 결과값을 저장할 변수 선언
    int result2;       // 결과값을 저장할 변수 선언

    /*
     *  동적 라이브러리 작업 순서
     *
     *  1. 동적 라이브러리 생성
     *  2. dlopen() 함수로 라이브러리 열기
     *  3. 라이브러리를 열지 못하면 dlerror() 함수로 에러 출력
     *  4. dlsym()  함수로 심볼정보를 찾아서 함수포인터에 넣기
     *  5. 함수 포인터 사용
     *  6. dlclose() 함수로 라이브러리 닫기
     */

    /*
     * 이 부분에서 동적 라이브러리를 열어서 사용한 것이다.
     * 일반적으로 동적 라이브러리는 프로그램이 참조하는 라이브러리에 위치한다.
     * iptables: /usr/lib64/xtables/
     * pam: /usr/lib64/security/
     */
    
    // 여기서는 현재 디렉터리에 동적 라이브러리를 생성해서 사용한다.
    lib  = dlopen("./libmyfunction.so",RTLD_LAZY);  // 2. 라이브러리 열기
    if(!lib) // 3. 라이브러리 에러 출력
    {
        fprintf(stderr,"%s\n",dlerror());
        return 1;
    }
    plus = dlsym(lib,"plus");     // 4. 심볼정보 plus 찾기
    minus = dlsym(lib,"minus");   // 4. 심볼정보 minus 찾기
    result1 = plus(7);            // 5. 함수 포인터 사용
    result2 = minus(5);           // 5. 함수 포인터 사용

    printf("result1 = %d result2 = %d\n", result1, result2);  // 얻어진 결과값 출력 8, 4

    dlclose(lib); // 6. 라이브러리 닫기

    return 0;
}
-- main.c --


# gcc -fPIC -c minus.c plus.c
# gcc -shared -Wl,-soname,libmyfunction.so -o libmyfunction.so plus.o minus.o
# nm libmyfunction.so
  :
  :(생략)
00000000000006a4 T minus  <-- minus 함수
0000000000000695 T plus   <-- plus  함수
00000000000005e0 t register_tm_clones

# gcc -o main main.c -ldl
# ldd main
        linux-vdso.so.1 =>  (0x00007ffdfbd70000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fbe6dfec000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fbe6dc1e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fbe6e1f0000)
# ./main
result1 = 8 result2 = 4

실습> 패키지및 소스 패키지 다운로드

참고: https://cafe.naver.com/linuxmasternet/1655

배포판 : CentOS 7.9.2009, Rocky Linux 8.4

패키지를 다운로드 받는 방법에는 yum과 yumdownloader 명령어를 이용하는 두 가지 방법이 있다.

1. yum을 이용하는 방법
형식 :  yum install mc --downloadonly --downloaddir=directory
옵션 :
--downloadonly : 패키지를 설치하지 않고 다운로드하는 옵션
--downloaddir=directory : 다운로드할 디렉터리를 설정하는 옵션 (이 옵션이 없으면 /var/cache/yum/x86_64/7/ 에 다운로드 된다.)

사용법 : 
현재 디렉터리에 mc 패키지를 다운로드 한다.
# yum install mc --downloadonly --downloaddir=.

2. yumdownloader를 이용하는 방법
Yum 저장소에서 RPM 패키지를 다운로드하는 명령어로 yumdownloader를 사용하기 위해서는 yum-utils 패키지를 설치해야 한다.

형식 : 
yumdownloader --downloadonly 패키지명: 패키지를 다운로드 한다.
yumdownloader --downloadonly --source 패키지명: 소스 패키지를 다운로드 한다.

옵션 : 
--destdir=DESTDIR: DESTDIR로 다운로드
--resolve: 의존성 있는 패키지 모두 다운로드
--source : 소스 패키지 다운로드

CentOS 7.9.2009에서 사용법: 
- yum -y install yum-utils
- yumdownloader --downloadonly coreutils
- yumdownloader --downloadonly --source coreutils

Rocky Linux 8.4에서 사용법:
- dnf -y install yum-utils
- yumdownloader --downloadonly coreutils
- yumdownloader --downloadonly --source coreutils

yum-utils 패키지를 설치한다.
# cd
# yum -y install yum-utils
# yumdownloader --downloadonly coreutils
# yumdownloader --downloadonly --source coreutils
# useradd mockbuild
# rpm -Uvh coreutils-8.22-24.el7_9.2.src.rpm 
# cd rpmbuild/SOURCES
# tar xJf coreutils-8.22.tar.xz
# cd coreutils-8.22
# ls -l

root 권한으로 configure를 하면 에러가 발생하므로 여기서는 소스를 분석하는 목적이므로 환경변수 FORCE_UNSAFE_CONFIGURE 를 설정한다. 
또한 중요한 것은 컴파일해서 소스코드를 분석하는 목적을 가지고 있으므로 make install은 생략한다.
# export FORCE_UNSAFE_CONFIGURE=1 
# ./configure 
# make
# cd src
# vi ls.c

1240 int
1241 main (int argc, char **argv)                                                               
1242 {
  :
  :(생략)
1277   printf(">>> 소스 파일 수정 <<<\n"
1278          ">>> Written by Linuxmaster.net <<<\n\n");
1279 
1280   initialize_main (&argc, &argv);


# cd ..
# make
# cd src
# ./ls
[root@localhost src]# ./ls
>>> 소스 파일 수정 <<<
>>> Written by Linuxmaster.net <<<
  :
  :(생략)
profile
정보보안 전문가

0개의 댓글