본문 바로가기
정보보안

정보보안1 6차

by IT매니절 2024. 4. 21.

 

정적 라이브러리 경로 ( 컴파일시 정적 라이브러리가 있어야, 정적으로 컴파일 할 수 있다 )

64bit 환경 경로: /usr/lib64/libc.a
32bit 환경 경로: /usr/lib/libc.a

 

mv /usr/lib/libc.a .
gcc -m32 -static myprogram.c -o myprogram_ttt

에러 출력
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status

→ 32bit 환경 정적 라이브러리 경로를 옮기자 컴파일 에러가 난다

 

64비트 파일 경로 이동 테스트

 

 

아카이브 파일archive file
- 여러 파일이나 디렉토리를 하나의 단일 파일로 묶은 형태
- 압축과 달리 파일이나 디렉터리를 그대로 묶음

리눅스 ar 명령
- 리눅스에서 아카이브 파일 생성시 사용. 주로 정적 라이브러리를 만들때 사용한다.

 

ar 명령어 옵션

-r : 아카이브에 파일 추가

-c : 새로운 아카이브를 만들 때. 존재하면 덮어씀.

-s : 아카이브 파일 정렬

 

ar rcs 만들파일명.a 파일명.o 파일명.o 파일명.o ...

 

ex)

ar rcs testAr.a myprogram1 myprogram2
ll
-rw-r--r--. 1 root root   35084  4월 21 14:23 testAr.a

( 파일크기가 myprogram1 myprogram2 두 파일의 합보다 크다 )

 

ar 명령어로 라이브러리 내부 오브젝트 파일 리스트 확인 ( t )

 

ar t /usr/lib/libc.a printf.o

→ 특정 오브젝트 파일이 있는지 확인할 수 있음

 

rpm -qf /usr/lib/libc.a
glibc-static-2.28-236.el8_9.12.i686

rpm -qf /usr/lib64/libc.a

glibc-static-2.28-236.el8_9.12.x86_64

( rocky8 기준 컴파일시 참조하는 32비트, 64비트 버전들 )

 

정적 라이브러리의 생성 방법
- 소스 파일들을 -c 옵션을 통해 컴파일한다.
- 컴파일로 나온 object 파일들을 ar 명령어로 아카이브 파일로 생성한다.

 

 

소스 파일을 정적 라이브러리로 컴파일 하는 방법
gcc -c 소스파일명.c -o 실행파일명 -ltest -L.
→ 소스파일명.c를 -o 실행파일명으로 컴파일하는데, test라는 라이브러리로 컴파일-l 하고, 그 라이브러리는 현재 디렉터리. 에 있다-L

 

vi a.c
vi main.c (내용에 void a(); 선언 후 a(); 실행 소스가 들어있다)

void a();
int main(){
  a();
  printf("main~");
  return 0;
}



gcc -c a.c
gcc -c main.c
gcc -o main main.o a.o (-lc 가 생략됨. c 라이브러리로 컴파일 한다는 뜻)
./main
→ gcc로 a.c와 main.c를 -c 옵션으로 컴파일한다.
gcc로 main.o a.o 두 오브젝트파일을 -o 옵션으로 묶어서 main 실행파일을 만든다
main 실행파일을 실행하면, a의 소스코드와 main의 소스코드가 둘 다 출력된다.

생성된 a.o main.o main 파일

 

 

gcc -c a.c b.c c.c
ar rcs libmyfunc.a a.o b.o c.o
a.c, b.c, c.c 소스파일을 gcc -c 옵션으로 컴파일 한다.
ar 명령어로 libmyfunc.a 아카이브 파일로 묶는다

 

main2.c 파일과 libmyfunc.a 아카이브 파일을 하나로 컴파일 하려면 두 가지 방법이 있다

1. 라이브러리를 명시하기
gcc -o main2 main2.c libmyfunc.a

→ main2.c 소스파일과, libmyfunc.a 아카이브 파일을 -o 옵션으로 컴파일하여 main2 실행파일을 생성한다.

2. gcc -l, -L 옵션 사용하기
gcc main2.c -o main2 -lmyfunc -L. ( -lc 생략됨 )

→ main2.c 파일을 -o로 컴파일해서 오브젝트로 만들고, myfunc 라이브러리 파일과 컴파일-l 하겠다. 해당 라이브러리 파일은 . 현재 디렉터리에 있다-L

( static : gcc -static main2.c -o main2_static -lmyfunc -L.

 32비트 컴파일은 ? )

 

ldd main
        linux-vdso.so.1 (0x00007fff8f7e2000)                 ← ldd 실행시마다 메모리주소값이 달라짐
ldd main
        linux-vdso.so.1 (0x00007ffd9e9fc000)                 ← ldd 실행시마다 메모리주소값이 달라짐

ASLR 주소 랜덤화 ( 해킹 방어를 위한 메모리 보호 기법 )

 

 

-rwxr-xr-x. 1 root root   18192  4월 21 15:22 main2
-rwxr-xr-x. 1 root root 1708248  4월 21 15:24 main2_static

→ 동적 컴파일의 파일 크기와, 정적 컴파일의 파일 크기 비교

 

공유 라이브러리
실행파일 안에 오브젝트가 모두 들어가는 정적 라이브러리는 파일의 크기가 커지기 때문에 HDD 공간에 대한 효율이 떨어진다.
그 단점을 보완하기 위해 나온 공유 라이브러리.

 

공유 라이브러리 시스템에 인식시키기
1. 시스템 라이브러리 공유 폴더에 파일 넣기 ( /usr/lib64 또는 /usr/lib root 경로. 권한 필요 )
2. /etc/ld.so.conf 에 공유 라이브러리 디렉토리 설정 ( root 권한 필요 )

 - ldconfig 명령어를 실행필요
3. LD_LIBRARY_PATH 환경변수에 공유 라이브러리 디렉토리 설정 ( 일반 유저도 가능 )

 

공유 라이브러리 생성
1. 소스코드 작성 ~.c
2. 오브젝트 파일로 컴파일 ~.o
3. 공유 라이브러리 생성 ~.so.1.0.0
4. soname 생성 ~.so.1 -> ~.so.1.0.0
5. linkername 생성 ~.so -> ~.so.1.0.0

 

공유 라이브러리 : lib라이브러리명.so.주번호.부번호.패치번호

 

공유 라이브러리 생성은 gcc -fPIC 옵션을 활용한다. (컴파일된 코드가 메모리에서 재배치 가능하도록 하라는 옵션)
gcc -fPIC -c a.c b.c c.c

gcc -shared -Wl,-soname,libmyfunc.so.1 -o libmyfunc.so.1.0.0 a.o b.o c.o
→ -shared 공유라이브러리 생성
→ -WI,-soname,libmyfunc.so.1 소속이름을 libmyfunc.so.1(주번호까지의 파일명)로 설정
→ 출력파일을 -o libmyfunc.so.1.0.0 으로 이름 지정
→ a.o b.o c.o는 실행파일을 이루는 오브젝트파일들 리스트

 

ln -s  libmyfunc.so.1.0.0 libmyfunc.so.1
ln -s  libmyfunc.so.1.0.0 libmyfunc.so

→ 심볼릭 링크를 만든다

 

ll

lrwxrwxrwx. 1 root root    18  4월 21 15:54 libmyfunc.so -> libmyfunc.so.1.0.0
lrwxrwxrwx. 1 root root    18  4월 21 15:54 libmyfunc.so.1 -> libmyfunc.so.1.0.0
-rwxr-xr-x. 1 root root  8304  4월 21 15:51 libmyfunc.so.1.0.0

 

gcc -o main main.c -lmyfunc -L.
→ main.c 파일과 myfunc 라이브러리를 컴파일하여 main 실행파일 생성

( 심볼릭 링크를 만들지 않으면, .so 파일이 없기 때문에 심볼릭링크까지 걸어줘야 gcc로 인식할 수 있음 )

 

./main
./main: error while loading shared libraries: libmyfunc.so.1: cannot open shared object file: No such file or directory

→ 실행시 libmyfunc.so.1를 찾지 못해 에러 발생. 시스템에 공유라이브러리를 인식시켜줘야 한다. 

 

시스템에 공유라이브러리 인식시키기

1. 시스템 라이브러리 디렉토리로 옮기기 ( mv 명령어, root 권한 필요)

mv libmyfunc.so* /usr/lib64
./main
a()
main()

바로 인식되어 실행가능해진다.

 

2. /etc/ld.so.conf 파일을 참조하여 해당 경로에 파일 추가 ( ldconfig 명령어, root 권한 필요)

/etc/ld.so.conf 파일을 열어보면,
include ld.so.conf.d/*.conf
이러한 경로만 한 줄 기록되어 있다.

/etc/ld.so.conf.d 디렉터리에 .conf 설정파일을 생성하고 그 안에 경로를 기록한다
그 후 ldconfig /etc/ld.so.cache 파일을 업데이트 해주어야 한다. (업데이트 해주지 않으면 인식X)

 

strings /etc/ld.so.cache | grep 2.SharedLibrary
/root/LinuxLibrary/2.SharedLibrary/libmyfunc.so
/root/LinuxLibrary/2.SharedLibrary/libmyfunc.so.1

cache 파일에 등록된 것 확인 ( binary 파일이므로 바이너리 파일에서 텍스트 형식을 검색하는 명령어 strings 사용 )

 

어째서 root 권한이 필요한가?

drwxr-xr-x. 2 root root 53  2월 21 03:09 /etc/ld.so.conf.d
-rw-r--r--. 1 root root 17303  4월 20 17:26 /etc/ld.so.cache
-rwxr-xr-x. 1 root root 959688  2월 21 03:11 /usr/sbin/ldconfig

  ldconfig 명령어 실행 권한은 있지만,

ld.so.conf.d 폴더는 other 일반유저의 w생성 권한이 없다

ld.so.cache는 other 일반유저의 wx생성,접근 권한이 없다

 

일반유저의 ldconfig 실행 결과 퍼미션 에러

 

 

3. LD_LIBRARY_PATH 환경변수 설정 ( 일반유저도 환경변수를 사용할 수 있다 )

 

export LD_LIBRARY_PATH=.
./main
a()
main()

 

./test/main
./test/main: error while loading shared libraries: libmyfunc.so.1: cannot open shared object file: No such file or directory

→ 하지만 다른 곳에서 실행시키면 찾지 못하므로, 가급적 절대경로로.

 

export LD_LIBRARY_PATH=/user1/LinuxLibrary/test

( 또는 export LD_LIBRARY_PATH=$(pwd)

또는 export LD_LIBRARY_PATH=`pwd` )

./test/main
a()
main()

→ 절대경로로 넣어주자 다른 폴더에서 실행해도 정상 실행

 

file 명령어에서 ELF 64-bit LSB executable : 64비트 실행 파일

shared libraries : 공유 라이브러리

 

동적 라이브러리

프로그램이 시작run 될 때가 아니라 필요할 때 적재 시키는 라이브러리
공유 라이브러리와 달리, 프로그램 링크시/시작시 적재되지 않는다

소스코드에서 dlopen(),dlsym(), dlclose() 등의 함수를 이용해 라이브러리를 열거나, 함수(심볼)를 찾거나, 라이브러리를 닫거나 한다.

* 소스파일에 반드시 dlfcn.h 헤더가 들어가야 하고, 컴파일에는 -ldl 옵션을 사용한다.

대표적으로 iptables, PAM, 아파치 웹서버의 모듈들도 동적 라이브러리이다.

                   └xtables   └security        └httpd/modules

                    (/usr/lib 또는 /usr/lib64 안에 위치)

 

lsmod : 시스템에 커널이 로드된 모듈 확인
rmmod : 로드된 커널 모듈 삭제

 

iptables에 룰을 추가하자, xt_nat 모듈이 로드되었다. 1은 사용중이라는 뜻

 

 

file plus.o

ELF 64-bit LSB relocatable : 실행 가능한 프로그램이나 라이브러리, 객체 등을 저장할 때 사용. 위치에 상관없이 올라간다는 뜻.

gcc -fPIC -c minus.c plus.c

gcc -shared -o libtest.so minus.o plus.o

 

ldd ./main
        linux-vdso.so.1 (0x00007fff357f7000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f53ea05d000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f53e9c98000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f53ea261000)

 

소스코드 내 dlopen
dlsym plus.c 심볼 정보 찾기

 

소스코드 내 상대경로로 되어 있으므로, 실행위치가 달라지면 실행할때 에러가 생긴다.

( ~  cannot open shared object file: No such file or directory )

 

 

확장자 요약

정적 라이브러리 : lib라이브러리명.a
공유 라이브러리 : lib라이브러리명.so.주번호.부번호.패치번호
동적 라이브러리 : lib라이브러리명.so

 

 

 

번외 실습

 

LD_PRELOAD로 선언시,
프로세스는 libc.so와같은 라이브러리보다 LD_PRELOAD를 먼저 바라본다.

 

1. test.c 에서 printf로 메세지를 출력한다.
2. hook.c에서 prfintf(char* a){ puts("내용"); puts(a); } 작성
→ test.c에서 사용하는 printf을 가로채기(후킹) 위해 hook.c에서도 printf를 선언해주었음. "내용"을 먼저 출력하고 puts(a) 의 a인 test.c의 내용을 출력하게 됨.

( puts(a); 코드가 없다면 본래 printf 는 출력되지 않는다. "대체" 하는 것이기 때문 )

 

3. 컴파일
gcc -o test test.c
gcc -fPIC -shared -o hook.so hook.c
export LD_PRELOAD=./hook.so

→ 공유 라이브러리로 생성한 후, LD_PRELOAD에 hook.so를 선언

 

결과

./test
Hooking test!
Hello World

→ hook.c 의 내용인 " Hooking test! " 문자열이 먼저 출력되고 test.c의 printf 내용이 출력되었다.

 

실습 참고한 블로그 : https://ar9ang3.tistory.com/8

'정보보안' 카테고리의 다른 글

정보보안1 8차시  (0) 2024.04.28
정보보안1 7차시  (0) 2024.04.27
정보보안1 5차시  (0) 2024.04.20
정보보안1 4차시  (0) 2024.04.14
정보보안1 3차시  (0) 2024.04.13