Forensic/Forensic 이론

디지털 데이터의 수 체계

Ron Weasley 2021. 7. 24. 15:37

저번 시간에 디지털 데이터 구성단위에 대해서 배웠습니다. 오늘은 디지털 데이터의 수 체계인 이진법, 십진법, 팔진법, 십육진법, 바이트가 저장되는 순서에 따른 빅엔디안, 리틀엔디안, 고정소수점, 부동소수점에 대해서 배워보겠습니다.

 

이진법이란?

  이진법은 디지털 데이터가 기본적으로 띄고있는 0과 1로 2가지만 표현하는 수 입니다. 일상생활에서 사용하는 수 체계인 십진법과는 다르지만, 디지털 포렌식을 공부하기 위해서는 꼭 알아야 할 수 체계입니다. 이진법은 변환 규칙을 이용하여 다른 수 체계로도 변환이 가능합니다.

 

  32비트의 체계로 생각을 해봅시다. 그럼 총 4byte = 32 bit = 2³² 입니다. 그림을 보고 설명을 해드리겠습니다.

총 32개의 칸이 존재하는게 보이시나요?? 이 칸마다 0 또는 1이 들어갑니다. 그럼 총 2³² 만큼 표현이되겠죠. 근데 첫번째 칸만 1이고 나머지가 다 0으로 채워질 때 2³² 만큼 표현이 되는것이고, 실질적으로는 첫번째 칸에 1을 뺀 두번째 칸부터 마지막 칸 까지 1로 채워진 수만 표현이 되는것입니다.

그래서  2³² - 1 만큼 표현이 되는것이지요. 하지만 정수에는 양의 정수와 음의 정수가 존재하는데요 그래서 맨 앞의 한칸을 +, - 사인으로 쓰게되는데 0일때 양수 1일때 음수입니다. 그러면 이제 양의 정수에서 사용할 수 있는 공간이 1개 줄어서 2³¹ - 1 만큼 표현이 되고 음의 정수일 때는 한 칸을 더 쓸 수 있기 때문에 -2³¹ 만큼만 표현이 됩니다. 이유는 양수에서 0을 표현했지만 음수에서는 0을 표현 안해도되기 때문입니다. 이제 8비트를 이진수로 표현하겠습니다.

 

<그림1> 1바이트를 표현하고 있는 이진수

이제 위의 그림인 이진수을 보면 1바이트(8bit)에 저장된 데이터를 나타내고 있습니다. 1바이트는 "00000000"부터 "11111111"까지의 값을 저장할 수 있고, 십진수로 255까지 표현이 가능합니다. 즉 1바이트는 0 ~ 255개의 수를 저장할 수 있으며, 더 많은 수를 저장하기 위해 앞 시간에 배웠던 구성 단위인 2바이트, 4바이트 등 2개 이상의 바이트가 필요합니다. 하지만 숫자에는 양수와 음수가 존재하죠?? 그럼 1byte는 -128 ~ 127까지의 수를 표현할 수 있습니다. 이제 그림에 나와있는 이진수를 십진수로 표현되는 공식을 알아봅시다.

10진수 2진법으로 변환

(어...그림이 이상하지만 그냥 봐주세요 ㅎㅎㅎ 마우스로 그린거라 못생겼네요...)

 

90이라는 정수를 이진수로 바꾸기 위한 방법입니다.

90을 2로 소인수 분해하면 그림처럼 나옵니다. 마지막에 몫이랑 각 계산마다 나오는 나머지를 다 이어주면 1011010이 되는데 양수를 표현하기 위해서는 제일 앞에 1비트는 "부호비트" 이므로 양수면 0, 음수면 1이됩니다. 그래서 90은 양수이기 때문에 0 1 0 1 1 0 1 0이 양수 90을 이진수로 변환한것입니다.

또 다른 예로 풀이해보자면

그림을 보면 왼쪽에서 부터 0 1 0 1 1 0 1 0을 표현하고 있습니다.

왼쪽에서부터 2^7, 2^6, 2^5, .... 2^0까지 표현을해서 총 0~255입니다.

그럼 위에 2진수를 보면

(0 * 2^7) + (1 * 2^6) + (0 * 2^5) + (1 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)

왼쪽에서부터 0 + 64 + 0 + 16 + 8 + 0 + 2 + 0 = 90을 표현하게 되는데

이렇게 값이 나온 90이 십진수입니다. 또 다른 예를 보겠습니다.

 

<그림2> 1바이트를 표현하고 있는 이진수

이 그림은 왼쪽에서 부터 1 0 1 1 0 0 0 1을 표현하고 있습니다.

위와 같은 계산식으로 풀어보면

(1 * 2^7) + (0 * 2^6) + (1 * 2^5) + (1 * 2^4) + (0 * 2^3) + (0 * 2^2) + (0 * 2^1) + (1 * 2^0)

왼쪽에서부터 128 + 0 + 32 + 16 + 0 + 0 + 0 + 1 = 177을 표현하게 되는데

이렇게 값이 나온 177이 십진수입니다. 이제 이해가 되시나요??

2^7 ~ 2^0까지 차례대로 계산해서 구하다보면 255까지 표현이 가능합니다.

 

그외의 진법들

|십진법

  - 우리가 일상생활에서 사용하는 수 체계로 0, 1, 2, 3, 4, 5, 6, 7, 8, 9를 의미합니다.

  - 일반적으로 이진법과는 다르게 10개의 숫자를 가지고 수를 표현하게됩니다.

 

|십육진법

  - 0, 1, 2, 3, 4 .... f까지 0부터 9까지의 수와 A부터 F까지의 로마 문자를 사용하고, 대소문자는 구별하지 않는다.

  - 디지털 데이터는 이진수를 사용하지만 데이터를 화면에 출력하거나 처리할 때 비효율적이기 때문에 니블(4비트)단위로 묶어서 표현하게 되는데, 0x0EA1 이런식으로 표현됩니다.

 

|팔진법

  - 0 ~ 7까지 총 8개의 숫자를 가지고 수를 표현하게 됩니다.

 

2진법 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111
10진법 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16진법 0 1 2 3 4 5 6 7 8 9 A B C D E F
8진법 0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17

디지털 데이터를 표현하는 수 체계인 이진법, 십진법, 팔진법, 십육진법을 다 배워보았습니다.

이제는 바이트의 순서를 표현하는 방법을 알아보겠습니다.

 

엔디안

  바이트 순서를 표현하기 위해 엔디안이라는 단어를 알아야합니다. 엔디안은 컴퓨터의 메모리와 같은 1차원 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻하고, 바이트를 배열하는 방법을 바이트 순서라고 합니다. 컴퓨터는 보통 32비트와 64비트로 표현 되는데, 엔디안은 평균적으로 큰 단위가 앞에 나오는 빅 엔디안과 작은 단위가 앞에 나오는 리틀 엔디안으로 나뉩니다. 순서가 왜 중요한지 모르는분들이 계실 수 있을거 같아서 말씀드립니다. 빅 엔디안과 리틀 엔디안 중 어떤 엔디안을 사용하는지에 따라 동일한 바이트 열에 대한 값이 전혀 달라지기 때문에 포렌식 분석에서 데이터를 확인할 때 데이터를 생성하는 프로세서와 응용 프로그램에서 사용하는 엔디안을 올바르게 파악해야 정확한 의미를 알 수 있습니다. 

 

빅 엔디안

  빅 엔디안 방식은 낮은 주소에 데이터를 높은 바이트 MSB(Most Significant Bit)부터 저장하는 방식입니다. 이 방식은 평소에 우리가 숫자를 적을 때 사용하는 방식과 같은 방식입니다. 쉽게 말해서 사람이 숫자를 읽고 쓰는 방법이 같아서 메모리상에 저장된 순서를 그대로 읽을 수 있으며, 리틀 엔디안에 비해서 이해 하기 수월합니다. 빅 엔디안을 사용하는 CPU로는 대표적으로 AMD, RiSC 계열, IBM 컴퓨터가 있습니다.

 

리틀 엔디안

  리틀 엔디안 방식은 낮은 주소에 데이터를 낮은 바이트 LSB(Least Significant Bit)부터 저장하는 방식입니다. 이 방식은 평소 우리가 숫자를 사용하는 방식과 반대로 거꾸로 읽어야합니다. 쉽게 말해서 사람이 숫자를 읽고 쓰는 방법을 반대로 읽고 메모리상에 저장된 순서도 반대로 읽으며, 빅 엔디안에 비해 이해 하기 어렵습니다. 리틀 엔디안을 사용하는 CPU로는 대표적으로 Intel 계열이 있습니다.

 

밑에 예제를 한번 보고 빅 엔디안으로 표현한 데이터와 리틀 엔디안으로 표현한 데이터를 비교해봅시다.

 

참고 : 메모리 주소는 가정을 한거기 때문에 컴퓨터마다 다릅니다.

저장 해야할 32비트 크기의 정수의 데이터 0x12345678
메모리 주소 0x100 0x101 0x102 0x103
변수 값 0x12 0x34 0x56 0x78

<빅 엔디안 표현 방식>

위에 표는 데이터를 그대로 변수 값에 순서대로 1바이트씩 들어가는것이 보이시나요?? 왜 1바이트라고 생각하시는분이 있을 수 있을겁니다. 이유는 32비트 크기의 정수 데이터입니다. 8개가 총 32비트인건데 메모리에는 1바이트씩 저장되어 표현이 되기 때문에 0x12, 0x34, ... 0x78로 나눌 수 있습니다. 지금 표현한 방식은 빅 엔디안 표현 방식인데, 우리가 읽을때도 0x12345678로 읽습니다. 이제는 리틀 엔디안으로 표현한 방식을 알아보겠습니다.

저장 해야할 32비트 크기의 정수의 데이터 0x12345678
메모리 주소 0x100 0x101 0x102 0x103
변수 값 0x78 0x56 0x34 0x12

<리틀 엔디안 표현 방식>

리틀 엔디안으로 표현한 <표>입니다. 리틀 엔디안은 낮은 주소인 0x100에서 부터 높은 주소인 0x103까지 변수 값을 저장할 때 낮은 바이트인 0x78부터 차례대로 0x56, 0x34, 0x12로 저장됩니다. 하지면 여기서 주의해야 할 점이 하나 있습니다. 처음 배우시는 분들은 뒤에서부터 저장되는줄 알고 0x87654321이렇게 저장이 된다고 생각할 수 있습니다. 하지만 1바이트씩 끊으면 12 34 56 78이니까 뒤에서 저장된다고 생각하면 78 56 34 12가 됩니다. 87 65가 아니구요! 그래서 표현하면 0x78563412가 됩니다.

 

이제 표현 방식도 알아보았으니 데이터에도 양수와 음수가 존재하고 정수와 실수가 존재합니다. 정수 데이터의 표현과 실수 데이터의 표현이 있습니다. 정수 데이터의 표현은 "고정 소수점"이라고 불리고, 실수 데이터의 표현은 "부동 소수점"이라고 불리웁니다. 

 

그전에 일단 알아둬야 할 것이 있습니다. 위에서도 설명했듯이 양수와 음수를 표현하는 "부호비트"에 대해서 배워보겠습니다.

 

부호 비트(MSB : Most Significant Bit , 최상위 비트)

  부호 비트는 01101001 이런식으로 표현된 비트패턴 중 가장 왼쪽에 있는 비트를 말합니다. MSB를 제외한 나머지 비트는 크기를 나타내는데 사용하는데, 부호비트가 0이면 양수, 1이면 음수로 표현됩니다. 그럼 위에 양수 90을 표현한 01011010을 11011010으로 바꾸면 -90인가요? 라고 질문을 할 수 있는데, 정답은 아니요 입니다. 왜냐하면 부호를 바꿀려면 비트를 2의보수 해줘야하는데 2의 보수는 밑에서 설명이 될것입니다.

 

고정 소수점

  | 정수 데이터를 표현하기 위해 사용되는 방식

  | 10진수를 2진수로 변환 후, 변환된 값을 그대로 정수부에 넣는 방식

출처 : https://tcpschool.com/cpp/cpp_datatype_floatingPointNumber

  32비트체계로 설명하겠습니다. 32비트의 수라면 젤 왼쪽에 1비트는 부호비트, 15비트는 정수 표현, 16비트는 소수 표현에 사용하는 식입니다. 젤 왼쪽에 1비트가 0이면 양수, 1이면 음수입니다. 소수점은 하지만 이러한 고정소수점 방식은 구현하기 편리하지만 사용하는 비트 수 대비 정수부와 소수부의 자릿수가 크지 않아서 표현 가능한 수의 범위나 정밀도가 낮아 실수형 데이터를 다룰 때는 사용하지 않습니다. 예제로 소수 10.625를 고정 소수점 칸에 맞춰서 설명 하겠습니다.

 

10.625를 2진수로 표현하면 1010.101입니다. 소수를 2진수로 표현하려면 다음과 같은 그림으로 하면되겠습니다.

소수 부분인 0.625를 2를 곱해서 정확히 소수자리가 0이 되게끔 만들어줍니다.

0.625 * 2 = 1.250 -> 0.250 * 2 = 0.5 -> 0.5 * 2 = 1.000이 됩니다. 정수와는 다르게 표현하는것은 위에서 부터 순차적으로 표현이 됩니다. 그래서 표현하면 0.101이됩니다. 고정 소수점으로 표현한다면 이런식으로 나올 것이다. (32비트 체계)


  고정 소수점 연산에서 음수를 표현하는 방법이 3가지가 있습니다. 부호와 절대치, 1의 보수, 2의 보수가 존재합니다.

표현 방법 설명
부호화 절대치 - 부호비트를 0에서 1로 1에서 0으로 바꾸어 표현하는것
(양수 표현에 대해서는 부호 비트를 0을 1로 바꿈)
- 범위 : - (2ⁿ-¹ - 1) ~ (2ⁿ-¹ - 1)
1의 보수 - 부호화 절대치 값에서 부호비트를 뺀 나머지를 반대로 표현하는 것
- 1의 보수 : 0 -> 1 , 1 -> 0 으로 변환
- 범위 : - (2ⁿ-1 -1 ) ~ (2ⁿ-¹ - 1)
2의 보수 - 1의 보수를 한 값에서 1을 더 해준 값
- 범위 : -2ⁿ-¹   ~ (2ⁿ-1 - 1)

표를 보면 부호화 절대치와 1의 보수는 표현할 수 있는 범위가 같은것을 알 수 있고, 2의 보수는 그에 비해 표현 범위가 1이 더 많다. 이제 간단한 예제를 하나 보겠습니다.

양수 10을 2진수 8비트로 나타낸 값 : 0 0 0 0 1 0 1 0
부호화 절대치 1의 보수 2의 보수
-10을 나타내기 위해 부호화 절대치로 계산을 하면 1 0 0 0 1 0 1 0이 됩니다.
(부호비트만 1로 바꿈)
1의 보수는 부호비트 빼고 다 변환을 시켜주는 것변환하면 1 1 1 1 0 1 0 1이다. 2의 보수는 1의 보수를 한 값
1 1 1 1 0 1 0 1에서 1을 더합니다.
1 1 1 1 0 1 1 0 입니다.
10을 부호화된 2의 보수로 표현한다면?
<풀이>
① 10진수 10을 2진수로 바꾸어 줍니다. ---------- 0 0 0 0 1 0 1 0
② 양수를 음수로 변환 하기 위해 부호비트를 전환합니다. ---------- 1 0 0 0 1 0 1 0
③ 1의 보수를 산출합니다. -------------- 1 1 1 1 0 1 0 1
④ 2의 보수를 산출합니다. -------------- 1 1 1 1 0 1 1 0 (1의 보수를 산출한 값에 1을 더해준것)
⑤ 그럼 10을 부호화된 2의 보수로 표현하면 -9가 됩니다.

부동 소수점

  | 실수 데이터를 표현하기 위해 사용

  | 고정 소수점 방식보다 넓은 범위의 수를 나타낼 수 있어 자주 이용됨

  | 고정 소수점 방식보다 연산 속도가 느림

출처 : http://tcpschool.com/java/java_datatype_floatingPointNumber

32비트 체계로 설명하겠습니다. 실수를 32비트의 수로 표현하면 float형 실수인데, 젤 왼쪽 1비트는 부호비트, 8비트는 지수부, 23비트는 가수부로 나뉩니다. 부동 소수점은 소수점이 움직인다는 뜻으로 소수점 자리가 움직입니다. 바로 예제로 위에 소수 10.625를 부동 소수점으로 표현을 해보겠습니다.

 

가수부를 먼저 채워보겠습니다. 10.625를 고정 소수점 2진수로 표현하면 1010.101입니다.

부동 소수점은 소수점이 옮겨진다고 했습니다. 이러한 방식을 정규화된 표현 방식이라고 합니다.

1.010101로 표현이 되겠죠?? 이제 소수점 뒤에 010101은 가수부(23비트)로 가게됩니다. 그리고 나머지 부분은 0으로 채웁니다.

이제 지수부를 채워야 하는데 지수부는 bias(바이어스)값을 더해줍니다.

Bias(바이어스) 값
32비트 127
64비트 1023

그럼 아까 구해놨던 지수는 3입니다. 지수 3에 bias값인 127을 더해주면 3 + 127이 됩니다.

그럼 130을 2진수로 변환해보겠습니다.

이렇게 1 0 0 0 0 0 1 0으로 됩니다. 그럼 변환된 지수를 8비트 지수부에 채워줍니다.

이로써 10.625를 32비트 단정도 부동소수점 방식으로 표현하면 11000001010100000000....00000으로 표현되는데 처음 배우시는분들은 어려울 수 있으나, 계속해서 숫자를 바꾸어주면서 익숙해질 때 까지 계산해보십시오. 제가 한 설명이 이해가 안된다면 다른 사람들이 포스팅 엄청 잘 포스팅 해놓은 것도 존재하니 한번 찾아 보시길 바랍니다. 제가 참고한 포스팅을 같이 올려드리겠습니다. 

 

참고자료

https://codetorial.net/articles/floating_point.html

https://st-lab.tistory.com/189

'Forensic > Forensic 이론' 카테고리의 다른 글

데이터 인코딩(Data Encoding)  (0) 2022.01.18
문자(Characters) 인코딩  (0) 2021.12.24
디지털 데이터의 구성 단위  (0) 2021.07.04
디지털 포렌식의 유형  (0) 2021.06.23
디지털 포렌식 수행절차  (2) 2021.06.01