Linux System Call
1. 이론
▶ 먼저 아래 그림을 보려고 한다. (그림 그리기 힘들다 ㅠㅠ)
▶ 위로 갈수록 하이레벨이고 아래로 갈수로 로우레벨이다.
(1) 이 그림으로 하고 싶은 말은 OS별로 제공되는 [Linux에서는 system call | Windows에서는 API]은 다르다.
(2) 예를 쉽게보면 우리가 프로그램을 설치할 경우에...
▶ Windows 7용, Windows 10용, Ubuntu18.04용, CentOS7 용 등등 같은 프로그램이지만 OS에 따라 다른 것을 다운 받는다.
▶ 그렇다면 하나의 프로그램을 다 같이 사용하면 안될까? 라는 의문이 든다.
(3) 아직은? 불가능하다..
[1] 이제 다시 그림을 보고 설명을 하려고 한다.
[2] 커널 위에 System call은 각각의 호출되는 번호가 있다.
[3] system call은 C언어보다 로우레벨이기 때문에 C언어의 라이브러리를 사용하는 것보다 불편함을 느끼게 된다.
[4] system call을 직접 사용시에 파라미터 요소를 다 알고 사용해야 하기 때문이다.
[5] 이 부분은 처음 시스템프로그래밍(커널서비스를 이용하는 프로그래밍)을 접한 사람이 느끼는 불편함이다.
[6] 문법도 낯설고 눈으로 직관적으로 와닿지 않기 때문에...
(4) 자바는..?
[1] 자바의 경우는 자바컴파일러가 .java를 .class로 컴파일하고 .class는 자바 바이트코드이다.
[2] 바이트코드는 기계어가 아니기 때문에 OS에서 바로 실행되지 않는다.
[3] 흔히 알고있는 자바가상머신이 OS가 바이트코드인 .class를 해석하도록 도와준다.
[4] 그래서 자바의 경우는 OS에 종속적이지 않다.
(5) Linux의 printf와 Windows의 printf??
[1] 내가 처음 C언어를 할 때는 이런 구조보다는 그냥 알고리즘 문제를 풀고 자바를 배워도 알고리즘문제를 풀고 GUI를 이용해 눈으로 보여지는 것에 관심이 있었다.
[2] 그러기 때문에 printf함수는 어디서 써도 같은 것을 사용하는줄 알았다.
[3] C표준 라이브러리는 해당 OS의 systemcall을 이용해서 작성되었다.
[4] 한마디로 리눅스에서 사용하는 printf와 윈도우즈에서 사용하는 printf의 라이브러리 내부구성은 다르다는 말이다.
[5] 결국 우리가 쓸 때는 만들어진 라이브러리를 사용하기 때문에 상관은 없다.
[6] 그러나 라이브러리를 제작해서 사용하거나 systemcall을 직접 사용할 경우는 분명 OS의 플랫폼 영향을 받을 것이다.
(6) 헤더파일은 무엇이고 라이브러리는 또 무엇일까?
[1] 헤더파일 (.h) - 사람이 읽고 해석할 수 있는 파일
▶ 쉽게보면 stdio.h를 쓰지 않으면 printf를 사용할 수 없는 것의 원리로 보아도 좋다.
▶ 한 마디로 어떤함수가 있는지 함수에 대한 정의를 적은 것과 같다고 생각해도 무방하다.
▶ Windows API를 하면서 헤더파일을 만들고 정의해주면서 모듈화 한 것을 생각해도 좋다.
[2] 정적라이브러리(.lib) - 라이브러리는 컴파일된 바이너리(기계어)
▶ 필요한 함수를 모두 가지고 있어서 실행파일만으로 독자적으로 돌아간다.
▶ 한 마디로 실행파일과 한 몸이라는 말이다.
▶ 만약 라이브러리를 수정하려면 몸을 분리할 수 없기 때문에 실행파일까지 새로 만들어서 배포해야한다.
▶ 또 실행파일이 여러 개 실행될 경우 메모리에 로드할 때 동일한내용이 중복해서 있기 때문에 효율적이지 못하다.
▶ 너무 단점만 말했는데 dll의 경우와 반대로 dll이 없거나 오류가 발생해도 문제 없이 혼자 잘 돌아간다.
[3] 동적라이브러리(.lib) - dll이 아님...
▶ dll의 어느 주소에 어떤 함수가 있는지에 대한 정보가 들어있다.
▶ 정적라이브러리는 다 들고 있기 때문에 무겁지만 동적라이브러리는 다른 프로세스와 공유하기 때문에 효율적이다.
▶ 동적라이브러리는 실행파일에 포함되지 않고 각각의 파일로 존재(커플과 비슷)하고 실행파일이 실행시 함께 로드(놀 때는 함께)된다.
▶ 동적라이브러리는 dll이 아니고 dll을 만들면 함께 생기는 lib와 같다.
▶ 생긴 lib는 실행파일 실행 시 dll을 호출(놀기 전 놀자고 부르는)하기 위한 정보가 들어있다.
▶ 만약 라이브러리 내용(필요한 함수의 내용)를 수정하려면 정적라이브러리처럼 하지 않고 dll만 수정해서 배포하면 된다.
▶ 동적라이브러리(lib)는 dll을 부르는 내용만 들어있기 때문이다.
[4] dll
▶ 실행파일이 자주 사용하는 함수를 따로 모아놓은 것이다.
▶ 내부구조가 실행파일과 거의 비슷하며 실행파일 실행 시 메모리에 로드된다.
▶ 실행파일은 메모리에 로드된 dll을 사용하여 공용으로 다른 실행파일과 함께 사용한다.
▶ 단 dll에 문제가 발생 시 실행파일도 정상적으로 돌아가지 않는다..
※ 조금 쉽게 이해하려면?
▶ 실행파일(나), dll(남자친구 혹은 여자친구), lib(메신저 혹은 전화)라고 생각하고...
▶ 실행파일이 실행될 때 (=내가 놀고 싶을 때)
▶ 동적라이브러리(lib)가 dll을 호출한다. (=전화로 남자친구 혹은 여자친구를 부른다)
▶ 실행파일이 메모리에 로드될 때 dll과 함께 로드된다. (=만나서 놀 때는 함께 논다)
▶ 프로세스가 종료되고 메모리에서 지워진다. (=다 놀고 각자 자기집 간다)
(7) 컴파일과 링킹
[1] Compile
▶ 프로그램 내의 문법적인 오류를 검사 후 오브젝트파일인 기계어 코드로 변환한다.
▶ 오브젝트파일의 확장자는 통상 .obj(Windows)를 사용하거나 .o(Linux)를 사용한다.
▶ 헤더파일을 따라가서 사용가능한지 확인하는 것과 같다.
[2] Linking
▶ 오브젝트파일이 실행파일이 된 후 사용해야 하는 함수(헤더파일에서 정의한 함수)를 정적 혹은 동적으로 연결한다.
▶ 즉, 라이브러리를 사용하려면 라이브러리의 헤더파일이 있어야 한다.
▶ 이유는 링커가 확인하는 심볼의 이름을 컴파일러가 만들어주고 그래야 라이브러리를 연결할 수 있기 때문이다.
▶ 가끔 컴파일에서는 오류가 없지만 링킹 시에 에러가 발생한다면 lib가 dll을 못 찾거나 dll에 정상적으로 구현이 안되거나 오염된 경우이다.
▶ 정적라이브러리는 오브젝트파일과 함께 링킹되어 하나의 실행파일이 완성된다.
▶ 동적라이브러리는 오브젝트파일과 dll을 호출하는 내용이 함께 링킹되어 실행파일이 실행 시 dll이 호출된다.
▶ 라이브러리 링크방법은 #pragma comment(lib, "[라이브러리이름].lib")를 적어주면 된다.
▶ Windows API로 음악재생하는 실행파일을 제작할 때 "Winmm.lib"를 사용하기도 했었다.
(8) 내 컴퓨터는 64비트인데 32비트 프로그램이 돌아가던데??
▶ 엄연히 말하면 돌아갈 수 없다.
▶ 이유는 64비트와 32비트의 주소체계도 다르지만 사용하는 system call 번호도 다르기 때문이다.
▶ 그러나 대단한 사람들이 64비트에서 32비트를 사용할 수 있도록 하였다.
- 먼저 이 그림은 아래 로고를 보면 알겠지만 가져온 것이다.
[1] 64비트환경 부팅 시 WoW64win.dll, WoW64.dll이 32비트의 ntdll.dll을 호출한다.
[2] 이후 32비트 환경에서 필요한 kernel32.dll, user32.dll등을 가져와서 그 위에서 32비트 프로세스가 동작한다.
[3] 만약 64비트환경에서 32비트 프로세스가 커널에 직접 엑세스한다면... 먹통이 될 가능성이 크다.
[4] 반대로 32비트환경에서는 64비트 프로세스가 돌아갈 수 없다.
[5] 32비트환경에서 접근할 수 있는 주소의 범위가 다르기 때문이다.
+ 어떤 기사에서 봤는데 요즘 32비트에서 돌아가는 64비트 악성코드가 있다고 했었던 것 같습니다.
+ 그 이름은 sodin이고 랜섬웨어의 한 종류입니다.
https://www.boannews.com/media/view.asp?idx=82356
+ 천국의 문(Heaven’s Gate)이라고 불리는 기술을 이용했답니다.
이 부분은 기사가 나오고 추가한 내용입니다.
※ 리눅스 시스템 콜에 대해 작성하려고 했는데 엉뚱한 곳으로 빠져서 서론만 길어졌다..
먼저 32비트환경에서 간단하게 사용하는 방법을 적어보려고 한다.
2. System Call
(1) 번호로 호출
▶ 위 화면은 32비트 환경에서의 리눅스 시스템 콜 번호와 사용방법을 나타낸 것이다.
(2) sys_write - 파일 혹은 화면에 쓰기
[1] 사용방법
▶ sys_write를 사용하려고 보면 eax에 호출할 시스템 콜의 번호, ebx에 파일 디스크립터, ecx에 출력할 내용이 저장된 메모리주소, edx에 출력할 버퍼의 사이즈(길이)를 넣으면 된다.
▶ 용어 설명을 간략히 하면 시스템 콜은 번호로 호출한다고 위에서 설명했다.
▶ 파일 디스크립터는 핸드폰 단축번호와 비슷한 원리로 생각해도 좋다.
+ 예를 들어 남자친구 혹은 여자친구에게 매일 전화를 해야하는데 전화할 때마다 번호를 찍어서 전송하면 번거롭고 불편하니 단축번호 2번으로 저장을 해 놓은 것이라고 생각하면 간단하다. (100%동일하지는 않지만 이해가 안간다면 원리는 비슷하다.)
+ 그러면 다음에 전화할 때는 숫자 2만 누르면 알아서 연결이 되기 때문이다.
+ 즉, 파일 디스크립터는 작업하기 용이하도록 파일을 열어놓은 상태를 저장하고 있으며 작업 후 해제해 주어야 한다.
▶ c언어로 보면 char buf[100] = "d4tai1"; 과 같이 초기화된 변수를 선언했다면 buf(시작주소)가 ecx에 들어가고, 100이 edx에 들어간다고 보아도 좋다.
[2] 소스(test1.asm)
▶ 텍스트영역과 데이터영역은 안다고 가정하고 설명하려고 한다.
▶ 6라인은 eax에 sys_write의 번호인 4를 넣는다.
▶ 7라인은 파일 디스크립터 즉 어디에 출력할 것인지를 작성한다.
▶ ebx에 화면(콘솔)에 출력하는 1을 넣는다.
▶ 8라인은 ecx에 출력할 문자열의 주소를 넣고
▶ 9라인은 edx에 출력할 문자열의 길이를 넣는다.
▶ 11라인은 시스템 콜을 호출(0x80)하기 위한 인터럽트이다.
▶ 17라인은 현재주소에서 msg의 시작주소를 뺀 값, 즉 msg의 길이가 저장된다.
[3] 시연
▶ -g 는 오브젝트파일에 디버깅(gdb 등)을 위한 정보(변수명, 모듈명)등을 추가 기록하는 옵션(배포용일 경우 사용x)이다.
▶ -o 는 생성할오브젝트파일의 이름을 지정할 경우 사용한다.
▶ -f 는 파일시스템 포맷을 설정하는 옵션이다.(elf는 리눅스, pe는 윈도우즈)
▶ -m32 는 32비트형태로 오브젝트파일을 생성한다는 의미이다.
(3) sys_open - 파일열기, 닫기
[1] 소스
▶ 5라인은 eax에 sys_open의 번호인 5를 넣는다.
▶ 6라인은 ebx에 오픈할 파일의 이름을 저장한 주소를 넣는다.
▶ 7라인은 ecx에 readonly를 의미하는 0을 넣는다.
+ 추가적으로 writeonly는 1, readwrite는 2, accessmode는 3을 의미한다.
▶ 8라인은 edx에 소유자, 소유자그룹, 일반사용자에게 부여할 권한을 8진수로 넣어준다.
▶ 9라인은 시스템 콜을 호출(0x80)하기 위한 인터럽트이다.
▶ 11라인은 시스템 콜의 반환결과인 파일디스크립터를 fd에 저장한다.
▶ 13라인은 eax에 sys_close의 번호인 6를 넣는다.
▶ 14라인은 ebx에 닫을 파일디스크립터(번호)를 넣는다.
▶ 15라인은 시스템 콜을 호출(0x80)하기 위한 인터럽트이다.
▶ 18라인은 파일이름을 저장한다.
[2] 시연
▶ 아무런 결과가 출력되지 않는다. 당연히 파일을 읽고 출력을 하지 않았기 때문이다.
(4) sys_creat - 파일생성 후 열기 및 닫기
[1] 소스
▶ 5라인은 eax에 sys_creat의 번호인 8를 넣는다.
▶ 6라인은 ebx에 생성할 파일의 이름을 저장한 주소를 넣는다.
▶ 7라인은 ecx에 소유자, 소유자그룹, 일반사용자에게 부여할 권한을 8진수로 넣어준다.
▶ 8라인은 시스템 콜을 호출(0x80)하기 위한 인터럽트이다.
▶ 10라인은 시스템 콜의 반환결과인 파일디스크립터를 fd에 저장한다.
▶ 11라인은 eax에 sys_close의 번호인 6를 넣는다.
▶ 12라인은 ebx에 닫을 파일디스크립터(번호)를 넣는다.
▶ 13라인은 시스템 콜을 호출(0x80)하기 위한 인터럽트이다.
[2] 시연
▶ 새로운 파일이 생성된 것을 확인할 수 있다.
(5) sys_read, sys_write - 입력 후 읽은 내용 출력
[1] 소스
▶ 6라인은 eax에 sys_read의 번호인 3를 넣는다.
▶ 7라인은 ebx에 읽을 파일의 핸들 값을 지정한다.
▶ 8라인은 ecx에 읽은 내용을 저장할 메모리 주소를 넣어준다.
▶ 9라인은 edx에 읽을 파일의 사이즈를 넣어준다.
▶ 13라인은 eax에 sys_write의 번호인 4를 넣는다.
▶ 14라인은 ebx에 저장할 파일의 핸들 값을 지정한다.
▶ 15라인은 ecx에 쓸 내용이 저장된 메모리 주소를 넣어준다.
▶ 16라인은 edx에 쓸 내용의 사이즈를 넣어준다.
[2] 시연
▶ 콘솔로 입력하였고, 입력한 내용이 출력되었다.
(6) 파일 복사하는 프로그램
[1] 소스
segment .text global main main: ; print start_msg ; write(1[stdout], msg_addr, msg_len); mov eax, 4 ; sys_write() mov ebx, 1 ; standard output mov ecx, start_msg ; buffer address mov edx, start_msg_len ; buffer size int 0x80 ; system call 호출 ; 읽기용으로 파일열기(open read file) ; open(filename_addr, 0[read_only], permission) mov eax, 5 ; sys_open() mov ebx, openfile ; filename address mov ecx, 0 ; read only mov edx, 0777o ; permissions int 0x80 ; system call 호출 mov [sfd], eax ; handle save ; 쓰기용으로 파일생성(creat the file) ; creat(filename_addr, permission) mov eax, 8 ; sys_creat() mov ebx, creatfile ; filename address mov ecx, 0644o ; permissions int 0x80 mov [dfd], eax ; handle save ; flie copy copy_start: ; read(file_descriptor, buffer_addr, buffer_size) mov eax, 3 ; sys_read() mov ebx, [sfd] ; file descriptor[열은 파일의 핸들 값]] mov ecx, buffer ; buffer address mov edx, 0x1 ; buffer size int 0x80 cmp eax, 0x00 je end_reading ; write(file_descriptor, msg_addr, msg_len); mov eax, 4 mov ebx, [dfd] ; file에 쓸 경우에는 file descriptor를 전달 mov ecx, buffer mov edx ,1 int 0x80 jmp copy_start end_reading: ; close(file_descriptor) mov eax, 6 ; sys_close() mov ebx, [sfd] ; open()이나 creat()로 생성한 file_descriptor int 0x80 mov eax, 6 mov ebx, [dfd] int 0x80 ; print end_msg mov eax, 4 mov ebx, 1 mov ecx, end_msg mov edx, end_msg_len int 0x80 ret segment .data openfile db 'readme.txt', 0x00 creatfile db 'write.txt', 0x00 start_msg db 'start filecopy', 0x0a, 0x00 start_msg_len equ $ - start_msg end_msg db 'end filecopy', 0x0a, 0x00 end_msg_len equ $ - end_msg segment .bss sfd resd 1 dfd resd 1 buffer resb 20
[2] 시연
▶ 정상적으로 파일이 복사된 것을 확인할 수 있다.
※ 이는 linux에서 c언어 프로그램을 작성할 때 #include<unistd.h>를 적고 사용할 수도 있다.
'Programming Language > Assembly' 카테고리의 다른 글
메모리와 변수 (0) | 2018.07.14 |
---|---|
레지스터 (0) | 2018.07.14 |
어셈블리어란? (0) | 2018.07.14 |
댓글