1. 구름 IDE로 리눅스 개발환경 만들기
다음과 같이 구름 IDE에서 pratice라는 container을 만들었고, 이 container은 아래와 같은 설정을 가지고 있다.
OS 부분을 보면 Ubuntu 18.04TLS, 즉 리눅스 환경으로 해당 Container이 구축되어 있는 것을 확인할 수 있다.
(추가적으로, C언어를 통해서 컴퓨터 구조 실습을 진행하기 위해서 C Console Project를 사용해주었다)
2. Sizeof 연산 타이핑 해보기
sizeof 연산자는 특정 변수 type이 몇바이트의 크기를 가지고 있는지를 출력해주는 역할을 한다.
이를 통해서 우리는 어떤 변수가 몇 바이트의 크기를 차지하는지 알 수 있는데, 이 부분을 잘못 사용하면 취약점을 발생시킬 수 있기 때문에, 각 변수의 크기를 잘 알고 알맞게 사용하는 것이 중요하다.
아래는 sizeof를 통해서 대표적인 변수 타입들의 크기를 알아본 결과이다.
위에서 사용된 %zu 형식 지정자는 부호가 없는 unsigned size_t의 값을 출력하기 위한 형식이라고 한다.
또한 데이터의 경로를 가르키는 pointer (void*)의 크기가 8 바이트라는 점도 눈여겨보아야 할 부분이라고 생각된다.
3. 오버플로 예제를 언더 플로로 바꿔서 해보기 (CHAR_MIN의 값에서 -1)
오버플로란 변수의 지정 타입 크기를 벗어난 데이터가 입력될 때 나타나는 현상이다.
예를 들어서 int가 4바이트의 크기를 가지고 있어서 –2,147,483,648 ~ 2,147,483,647 까지의 값을 가질 수 있는데, 최대 숫자인 2,147,483,647보다 큰 숫자가 입력되면 정상적이지 않은 값으로 변경이 되는 현상을 말한다.
거꾸로 최소 숫자보다 더 작은 값을 넣는 현상은 언더 플로라고 하기도 하는데, 정확하게는 언더 플로도 오버플로임으로 굳이 나누어서 쓸 이유는 없다고 한다.
아래 코드는 오버 플로와 언더플로를 직접 C언어에서 구현해본 예시이다.
오버플로 예시
언더플로 예시
위의 결과와 같이, 최대 -128~127까지의 값만 저장 가능한 char 타입에다가 범위를 벗어나는 값(최대/최소에서 +/-1)을 넣어주니 이상한 값이 출력되는 것을 알 수 있다. (최대값에서 1을 더하면 최소값으로 출력되고, 최소값에서 1을 빼면 최대값이 출력되는 패턴을 찾을 수 있다. 테스트 해본 결과 최소값에서 5를 빼면 123이 출력되는 것을 확인하였고, 넘친 수만큼 반대쪽 최대/최소 값에서 떨어진 수를 출력하는 것 같다는 추측을 해볼 수 있을 것 같다.)
4. 비트 연산 프로그램 바꿔보기 (특정 위치의 비트를 끄는 함수 구현, 사용자의 입력 (특정 위치 - int값)을 받도록 수정)
우선, 우리에게 주어진 코드는 다음과 같다.
우선, 이 코드를 조금 분석해보자.
is_bit_set 함수는 int를 반환하고, value와 position을 파라미터로 받는다.
리턴해주는 값은 return (value & (1 << position)) !=0; 인 것을 확인 할 수 있는데, 조금 복잡해보이는 식이다.
우선 전제되는 조건은 "비트 연산"이라는 점이다.
(1 << position) 은 position만큼 왼쪽으로 1이 움직인다는 뜻이다.
예를 들어서, 1은 2진수로 0000 0001인데, 이를 왼쪽으로 position만큼 움직인다는 뜻이다.
만약 position이 3이면 0000 1000이 될 것이다.
(value & (1 << position)) 은 value와 (1 << position) 을 AND연산 하라는 뜻이다.
& AND연산은 둘다 1이면 1, 둘중 하나라도 0이면 0을 반환하는 연산이다.
만약 value가 0000 1000이라고 하자.
position이 0000 1000임으로 둘을 AND연산 하면 0000 1000 나올 것이고,
만약 value가 0000 0001이였다면 position이 0000 1000 임으로, & 연산시에 0000 0000이 나올 것이다.
(value & (1 << position)) !=0 은 (value & (1 << position))의 값이 0인지 아닌지 검사한다.
만약 0이라면 value의 position이 0이였다는 뜻이고, 0이 아니라면 해당 위치는 1이라는 뜻이다.
위에서 본것과 같이 결국 position에는 한 비트를 제외한 모든 비트가 0임으로 value와 position에 같은 비트가 동시에 1이여야 0의 값을 피할 수 있다.
set_bit도 is_bit_set과 비슷하게 같은 파라미터를 받고, unsigned char을 반환한다.
return 코드인 return value | (1 << position); 을 해석해보면
(1 << position) position만큼 왼쪽으로 1비트를 이동하고
그것을 OR 연산을 value 값과 실행한다.
원래 value였던 0000 1000 = 8과 position을 2칸 왼쪽으로 옮긴 0000 0100=4 을 | 연산하면 0000 1100 = 12 가 반환될 것이다.
특정 위치의 비트를 끄는 함수 구현, 사용자의 입력 (특정 위치 - int값)을 받도록 수정
이제 위의 주어진 코드를 조금 수정해보자.
일단 특정 위치의 비트를 끄는 함수는 clear_bit 이라고 새로 하나 추가해주겠다.
형식은 위의 set_bit, is_bit_set과 비슷하게 같은 인자를 받아주고, unsigned char을 리턴해주겠다.
return 값을 정하는게 조금 어렵지만 차근차근 해보자
일단 value를 그대로 0000 1000으로 가정하고, position의 값을 0으로 바꿔야 한다.
우선 position은 위와 같이 (1 << position)을 통해서 비트로 만들어 준 다음에, ~ NOT연산을 통해 0과 1을 반전시키면 0000 1000 이였던 비트가 1111 0111이 될 것이다. 이 값으로 value와 & AND연산을 진행해주면 0이 포함된 비트만 결과값이 0이 되면서 비트가 삭제 될 수 있을 것이다.
코드는 아래와 같다.
또한 position 값을 사용자에게로 부터 받기 위해서 main을 조금 수정해주겠다.
scanf를 추가해주면서, clear_bit 기능에 충실하기 위해서 예제에 있던 is_bit_set과 set_bit은 지워주고, clear_bit만 입력받은 position과 함께 사용해주도록 하겠다.
(value값은 00000101=5) 으로 바꿔주었다.
코드는 아래와 같다.
코드가 정상적으로 작동하여서 position을 원래 0이였던 왼쪽에서 3번 이동한 자리로 설정했을 때는 똑같이 5가 출력되고,
position을 원래 1이였던 왼쪽에서 2번 이동한 자리로 설정했을 때는 왼쪽에서 2번 이동한 자리가 0이 되면서 0000 0001 인 1이 출력된다.
5. C언어가 기계어가 되는 과정 직접 해보기
우리가 작성하는 소스코드 (c언어)가 실행파일이 되기까지는 아래와 같은 Step이 필요하다.
이 과정을 직접 해보겠다.
STEP
1. 헤더파일 (main.h) + 소스코드 (main.c)
---- 전처리 (Pre-processing) ----
2. 전처리 된 소스 코드 파일 (main.i)
---- 컴파일 (Compilatioin) ----
3. 어셈블리어 파일 (main.s)
---- 어셈블리 (Assembly) ----
4. 오브젝트 파일 (main.o) + 라이브러리 (main.a, main.so)
---- 링킹 (Linking) ----
5. 실행파일 (exe 등)
우선 다음과 같은 c코드를 작성하였다.
이를 전처리 하여 .i 파일로 만들어보도록 하겠다.
gcc -E main.c -o main.i 명령어를 통해서 main.c를 main.i로 만들 수 있다.
그럼 다음과 같이 main.i 파일이 생성된다.
이 파일은 Askii 코드 임으로, 우리가 읽을 수 있는 문자로 되어있다.
이제 main.i 파일을 컴파일해서 어셈블리어 코드 main.s 로 만들어보자
다음 명령어를 사용하면 된다.
gcc -S main.i -o main.s
우리가 보던 어셈블리어 파일이 생성되었다.
이 또한 Askii 형식이라 우리가 읽을 수 있다.
다음은 어셈블리 과정을 통해 오브젝트 파일 main.o로 만들어보자
다음 명령어를 실행하면 된다.
gcc -c main.s -o main.o
이제는 맨 처음에 적혀있듯이 ELF형식의 파일이라서 우리가 읽을 수는 없다 (몇몇글자 빼고는).
이제 링킹 과정을 통해서 우리의 최종목적인 실행파일을 만들어주자
명령어는 다음과 같다.
gcc main.o -o main
이 또한 ELF 형식으로써 우리가 읽을 수는 없다.
이 main은 실행파일이다.
그럼 도대체 이 ELF는 뭘까?
ELF란 Executable and Linkable Format으로써 유닉스와 유닉스 기반의 프로그램에서 사용되는 공통의 바이너리 파일 포맷이다.
실행 프로그램, 오브젝트 파일, 공유 라이브러리 등에 사용된다고 한다.
우리는 file * 명령어를 통해서 각 파일의 형식을 볼 수 있는데 아래와 같이 나온다.
main.c, main.i, main.s 까지는 우리가 읽을 수 있는 ACSII TEXT이고, main.o와 실행파일 main은 ELF 형식으로 되어있는 것을 확인할 수 있다.
이 ELF형식으로 된 파일들을 010editor (hex 에디터)에서 열면 다음과 같이 보인다.
둘다 그렇게 많이 다르지는 않아보인다.
2023/Sep/10
'Study Log > 정보 보안' 카테고리의 다른 글
[정보 보안] 암호학 기초 강의 정리 (비공개) (0) | 2023.09.11 |
---|---|
[정보 보안] 3. 문서파일 포렌식 실습 (0) | 2023.09.11 |
[정보 보안] 정보보안 윤리 강의 정리 (비공개) (0) | 2023.09.11 |
[정보 보안] 2. 컴퓨터 구조 기초 (0) | 2023.09.11 |
[정보 보안] 1. 프로그래밍 기초 (1) | 2023.09.11 |