들어가며
서론
자료형은 변수의 크기를 정의하고, 용도를 암시한다.
자 예를들어 int 형으로 변수를 선언하면, 그 변수는 4바이트 크기고 정수 연산에 사용될 변수이다.
근데 자료형 담고 있는 정보가 컴파일러에도 전달이 되는데
변수의 자료형으로 고려해서 변수에 관한 코드르 생성한다.
(int는 4바이트, char은 1바이트 /연산은 그 메모리 공간을 대상으로 )
하번 정의된 변수의 자료형은 바꿀수 없다.
변수에 할당된 메모리의 크기는 확장되거나 줄어들지 않는다.
근대 여기서 만약
1바이트 크기의 변수에 1을 더하다가 0xff를 넘어가면 0x100이되냐
응 아닙니다.
0X00 이 된다.
이런형태를 데이터가 넘쳐따고 해서
overflow 라고 한다.
이것 처럼 변수의 크기보다 큰 값을 대입할떄도 데이터가 유실 될 수 있따.
(4바이트 변수에 0x0123456789abcdef를 대입하면 하위 4바이트인0x89abcdef만 들어감)
타입 에러
자료형
c언어에는 많은 자료형이 있따. (int, float, char...)
아까 자료형마다 저장할 수 있는 데이터 크기가 다르다고 했따
또한 운영체제 에 따라서도 크기가 달라질 수 있다.
32bit 운영체제 | 64bit 운영체제 | |
long | 4byte | 8byte |
이렇게 변수 자료형을 결정에는 변수에 담게 될 값의 크기, 용도, 부호 를 고려해야 한는데
type error은 이걸 고려하지 않은 것이다.
C언어 자료형의 크기와 용도
자료형 | 크기 | 범위 | 용도 |
(signed) char | 1 byte | -128 ~ 127 | 정수, 문자 |
unsigned char | 0 ~ 255 | 부호 없는 정수, 문자 | |
(signed) short (int) | 2 byte | -32,768 ~ 32,767 | 정수 |
unsigned short (int) | 0 ~ 65,535 | 부호 없는 정 | |
(signed) int | 4 byet | -2,147,483,648 ~ 2,147,483,647 | 정수 |
unsinged int | 0 ~ 4,294,967,295 | 부호 없는 정수 | |
size_t | 32bit: 4 byte 64bit: 8 byte |
32bit: 0 ~ 4,294,967,295 64bit: 0 ~ 18,446,744,073,709,551,615 |
메모리 크기나 배열의 크기를 나타낼 때 사용 |
(signed) long | 32bit: 4 byte 64bit: 8 byte |
32bit: -2,147,483,648 ~ 2,147,483,647 64bit: -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
정수 |
unsigned long | 32bit: 0 ~ 4,294,967,295 64bit: 0 ~ 18,446,744,073,709,551,615 |
부호 없는 정수 | |
(signed) long long | 64bit: 8 byte | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 정수 |
unsigned long long | 0 ~ 18,446,744,073,709,551,615 | 부호 없는 정수 | |
float | 4 byte | 1.175494e-38~3.402823e+38 | 실수 |
double | 8 byte | 2.225074e-308~1.797693e+308 | 실수 |
Type * | 32bit: 4 byte 64bit: 8 byte |
메모리 주소 값 | 포인터 |
👇예를 들어보자
//Name: type.c
//Compile: gcc -o type type.c
#include <stdio.h>
T factorial(unsigned int n) {
T res = 1;
for (int i = 1; i <= n; i++) {
res *= i;
}
return res;
}
int main() {
unsigned int n;
printf("Input integer n: ");
scanf("%d", &n);
if (n >= 50) {
fprintf(stderr, "Input is too large");
return -1;
}
printf("Factorial of N: %llu\n", factorial(n));
}
이런 코드가 있는데 여기서 자료형 T에는 뭐가 들어가야 할까?
T: unsigned long long
팩토리얼 함수는 n이 커질수록 매우큰 값이 나오게 되는데
n=20만 되도 int와 long을 넘어 선다. 그래서
64bit 시스템 에서 가장 크게 저장할 수 있는 unsigned long long이 맞다.
Out of Range: 데이터 유실
👇 아래는 앞의 예제에서 int res에 factorial 의 값을 반환하게 햇다.
실행결과
: 양수 곱하기 에서 값이 왜 작어졌을까?
res에 저장될 수 있는 범위보다 큰 값을 저장하려고 했기 때문이다.
아까 unsigned int 는 4바이트 라고 했다 그런데 factorial 함수가 반환하는 값은 8바이트 이다.
이 상황에서 usinged int에 값을 넣을 때 상위 4바이트는 버려지고 하위 4바이트만 남는다.
이렇게 변수에 값을 넣었는데 변수가 저장할 수 있는 범위를 넘어서면,
저장할 수 있는 최대치 까지 저장하고 나머지는 버린다.
Out of Range: 부호 반전과 값의 왜곡
👇앞의 예제랑 비슷한데 main 함수의 n의 자료형이 int로 바뀌었다.
(먼저 -1을 입력하면 23번쨰 출의 n>=50을 무시할 수 있따.)
실행결과
코드를 실행하고 -1을 입력하면 아무것도 안나온다.
왜냐하면
int n에 -1을 저장하면, n의 메모리 공간에 저장되는 값은 0xffffffff이다.
(이렇게 저장되는 이유가 궁금하다면 2의 보수에 대해 알아야 한다. 따로 정리할 거다.)
쨌든 factorial 함수는 unsigned int n을 인자로 받기때문에 -1이 아니라 4294967295로 전달되고
그러면 값이 너무 크고 시간도 오래걸려서 작동 되지 않는다.
(예방을 위해 양수로만 쓰이면 unsigned를 붙여야 한다.)
Out of Range 와 버퍼 오버플로우
👆 위에 코드는 잘못된 자료형으로 버퍼오버플로우로 이어진다.
하지만 사실 코드에선 32보다 작은지 검사해서 버퍼오버플로우를 막으려고 하고있지만
size의 자료형이 int이기 때문에 음수를 입력해서 검사를 우회할 수 잇따.
read함수의 마지막 인자의 자료형은 size_t 이다. 이건 아까 표에서 볼수 있듯이 부호가 없기떄문에
-1을 전해주게 된다면 매우 큰 수로 해석되어 버퍼오버플로우를 발생할 수 있다.
실제 32비트로 컴파일 해주면 버퍼 오버플로우가 발생한다.
💡64비트로 컴파일 하면 안됨?64비트에선 -1은 0xffffffffffffffff 이다. size_t는 64비트 환경에서 8바이트 크기의 부호 없는 정수를 나타내고,-1을 size_t로 환산하면 아주 큰 값이 된다. read함수는 이렇게 큰 값이 들어오게 되면 아무 동작도 하지 않고 에러를 뱉어넨다. |
타입 오버플로우와 언더플로우
연산중에 자료형의 범위를 넘어가면 변수값이 갑자기 작어나 커지는 상황이 있다.
이걸 Type Oveflow/Underflow라고 부른다.
정수 자료형을 대상으로 발생하면 Type에 Integer을 넣어 Integer Overflow/Underflow 라고 한다.
👇정수 오버플로우 / 언더플로우 예제 코드
그리고 이 예제를 실행하면,
오버플로우 일때는 자료형이 표현하는 최소값
언더플로우 일대는 최댓값이 되는 것을 알 수 있다.
실행결과
Interger Overflow와 버퍼 오버프로우
👇아래 코드는 interger overflow가 힙 버퍼 오버플로우로 이어지는 예제 코드이다.
사용자에게 size를 입력 받고 size+1 크기의 문자 포인터를 만든다.
그리고 그 포인터에 size 만큼 입력을 받는다.
이제 취약점을 찾아보면
size에 4294967295(unsigned int의 최대값)을 입력하면 오버플로우가 나서 size+1은 0이 된다.
그리고 그게 malloc에 전달하면, malloc은 최소 할당 크기를 할당한다.
(드림핵에는 32바이트 라고 하는데 16바이트 일수도 있고 잘 모르겠다)
그러나 read 함수는 size 값을 그대로 사용해서 힙 버퍼 오버플로우가 발생한다.
마지막
끝
'hacking > pwnable' 카테고리의 다른 글
[Assembly] c언어 코드를 어셈블리어로 변환하여 파일에 저장 (0) | 2025.03.02 |
---|---|
[Dream hack] Welcome Hackers👨💻 (0) | 2025.03.02 |
[개념 정리] plt와 got (0) | 2024.11.15 |
[Dream hack] Background: RELRO-2 (0) | 2024.11.15 |
[Dream hack] Quiz: RELRO-1 (0) | 2024.11.15 |