정적 라이브러리 경로 ( 컴파일시 정적 라이브러리가 있어야, 정적으로 컴파일 할 수 있다 )
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 환경 정적 라이브러리 경로를 옮기자 컴파일 에러가 난다
아카이브 파일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 /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의 소스코드가 둘 다 출력된다.
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생성,접근 권한이 없다
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 : 로드된 커널 모듈 삭제
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)
소스코드 내 상대경로로 되어 있으므로, 실행위치가 달라지면 실행할때 에러가 생긴다.
( ~ 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