Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save YangSiJun528/927a3d44511982a4038d6e020935a19e to your computer and use it in GitHub Desktop.

Select an option

Save YangSiJun528/927a3d44511982a4038d6e020935a19e to your computer and use it in GitHub Desktop.
[Jungle My Note | W11] Hex dump 읽기 치트시트.md

[Jungle My Note | W11] Hex dump 읽기 치트시트

예시:

000000004747ff90                          00 00 00 00 00 00 00 00
000000004747ffa0  00 00 00 00 00 00 00 00-da ff 47 47 00 00 00 00
000000004747ffb0  e8 ff 47 47 00 00 00 00-ed ff 47 47 00 00 00 00
000000004747ffc0  f7 ff 47 47 00 00 00 00-fb ff 47 47 00 00 00 00
000000004747ffd0  00 00 00 00 00 00 00 00-00 00 61 72 67 73 2d 6d
000000004747ffe0  75 6c 74 69 70 6c 65 00-73 6f 6d 65 00 61 72 67
000000004747fff0  75 6d 65 6e 74 73 00 66-6f 72 00 79 6f 75 21 00

1. 왼쪽 값은 그 줄의 시작 주소

000000004747fff0

이 줄의 첫 바이트 주소는:

0x4747fff0

오른쪽 바이트들은 순서대로 주소가 +1씩 증가한다.

주소          바이트
0x4747fff0    75
0x4747fff1    6d
0x4747fff2    65
...
0x4747fffb    79
0x4747fffc    6f
0x4747fffd    75
0x4747fffe    21
0x4747ffff    00

즉:

0x4747fff0 + 0xb = 0x4747fffb

2. 한 줄은 보통 16바이트

000000004747fff0  75 6d 65 6e 74 73 00 66-6f 72 00 79 6f 75 21 00

오프셋으로 보면:

offset  hex
+0      75
+1      6d
+2      65
+3      6e
+4      74
+5      73
+6      00
+7      66
+8      6f
+9      72
+a      00
+b      79
+c      6f
+d      75
+e      21
+f      00

따라서 0x4747fffb는 이 줄에서 +b 위치다.

3. Hex dump는 raw bytes일 뿐이다

Hex dump 자체에는 타입 정보가 없다.

같은 바이트도 어떤 타입으로 보느냐에 따라 다르게 해석된다.

바이트             해석
41                 uint8_t  0x41
41                 char     'A'
41 00 00 00        uint32   0x00000041
41 00 00 00        char[]   "A\0\0\0"

먼저 정해야 하는 것:

몇 바이트를 볼 것인가?
어떤 타입으로 해석할 것인가?

예:

1바이트씩 보면   char[] / uint8_t[]
4바이트씩 보면   uint32
8바이트씩 보면   uint64 / pointer

4. 구분자는 데이터가 아니다

00 00 00 00 00 00 00 00-da ff 47 47 00 00 00 00

중간의 -는 보기 좋게 8바이트씩 나눈 구분자다.

실제 메모리에 - 바이트가 있는 것은 아니다.

5. 포인터는 환경에 따라 크기가 다르다

64비트 환경에서는 포인터 하나가 8바이트다.

fb ff 47 47 00 00 00 00

이 8바이트가 포인터 하나일 수 있다.

64비트 환경: pointer = 8바이트
32비트 환경: pointer = 4바이트

포인터도 결국 메모리에 저장된 multi-byte 값이다.

6. Little-endian이면 multi-byte 값은 뒤집어서 해석한다

메모리에는 이렇게 보이지만:

fb ff 47 47 00 00 00 00

이 8바이트를 하나의 값으로 해석할 때는 little-endian 규칙 때문에 바이트 순서를 뒤집어서 읽는다.

메모리 바이트: fb ff 47 47 00 00 00 00
값 해석:       0x000000004747fffb

앞의 0은 생략할 수 있다.

0x4747fffb

이 값이 포인터라면 의미는:

주소 0x4747fffb를 가리킨다

핵심:

little-endian은 "주소만" 뒤집는 게 아니다.

2바이트, 4바이트, 8바이트 같은 multi-byte 값을
하나의 숫자로 해석할 때 적용된다.

예:

바이트                         타입       값
34 12                          uint16     0x1234
78 56 34 12                    uint32     0x12345678
fb ff 47 47 00 00 00 00        uint64     0x000000004747fffb

문자열의 경우:

79 6f 75 21 00

이것을 char[]로 해석하면 각 요소가 1바이트다.

79 = 'y'
6f = 'o'
75 = 'u'
21 = '!'
00 = '\0'

따라서 순서대로 읽으면:

"you!\0"

정리:

uint16, uint32, uint64, pointer:
  여러 바이트를 하나의 값으로 해석하므로 endian 적용

char[], uint8_t[], raw bytes:
  1바이트 단위 요소들의 배열로 해석하므로 뒤집을 대상이 없음

예시의 argv, argc 해석하기

7. argv 배열로 해석하기

이 부분은 8바이트 포인터들이 연속으로 들어 있는 영역으로 볼 수 있다.

000000004747ffa0  00 00 00 00 00 00 00 00-da ff 47 47 00 00 00 00
000000004747ffb0  e8 ff 47 47 00 00 00 00-ed ff 47 47 00 00 00 00
000000004747ffc0  f7 ff 47 47 00 00 00 00-fb ff 47 47 00 00 00 00
000000004747ffd0  00 00 00 00 00 00 00 00-00 00 61 72 67 73 2d 6d

8바이트씩 끊으면:

주소          8바이트 값                       값 해석
0x4747ffa0    00 00 00 00 00 00 00 00          0x0
0x4747ffa8    da ff 47 47 00 00 00 00          0x4747ffda
0x4747ffb0    e8 ff 47 47 00 00 00 00          0x4747ffe8
0x4747ffb8    ed ff 47 47 00 00 00 00          0x4747ffed
0x4747ffc0    f7 ff 47 47 00 00 00 00          0x4747fff7
0x4747ffc8    fb ff 47 47 00 00 00 00          0x4747fffb
0x4747ffd0    00 00 00 00 00 00 00 00          0x0

여기서 argv0x4747ffa8부터 시작한다고 보면:

argv 슬롯 주소     저장된 값         의미
0x4747ffa8         0x4747ffda        argv[0]
0x4747ffb0         0x4747ffe8        argv[1]
0x4747ffb8         0x4747ffed        argv[2]
0x4747ffc0         0x4747fff7        argv[3]
0x4747ffc8         0x4747fffb        argv[4]
0x4747ffd0         0x0               argv[5] = NULL

주의:

0x4747ffa8, 0x4747ffb0 ... 은 argv 포인터들이 저장된 위치다.
그 안에 들어 있는 값인 0x4747ffda, 0x4747ffe8 ... 이 실제 문자열 주소다.

argv는 보통 NULL로 끝나는 포인터 배열이다.

따라서 NULL 전까지 포인터가 5개이므로:

argc = 5

단, 여기서 argc 값을 직접 읽은 것은 아니다.

argv 배열의 NULL 전까지 포인터 개수를 세어서 argc를 역산한 것이다.

8. 각 포인터가 가리키는 문자열 읽기

문자열 영역:

000000004747ffd0  00 00 00 00 00 00 00 00-00 00 61 72 67 73 2d 6d
000000004747ffe0  75 6c 74 69 70 6c 65 00-73 6f 6d 65 00 61 72 67
000000004747fff0  75 6d 65 6e 74 73 00 66-6f 72 00 79 6f 75 21 00

포인터별 문자열:

포인터 값       문자열
0x4747ffda      "args-multiple"
0x4747ffe8      "some"
0x4747ffed      "arguments"
0x4747fff7      "for"
0x4747fffb      "you!"

확인 예시:

0x4747fffb -> 79 6f 75 21 00 -> "you!"

C 문자열은 \0에서 끝난다.

79 6f 75 21 00
 y  o  u  ! \0

9. 최종 argv 구조

argc = 5;

argv[0] = "args-multiple";
argv[1] = "some";
argv[2] = "arguments";
argv[3] = "for";
argv[4] = "you!";
argv[5] = NULL;

메모리 구조로 보면:

argv 슬롯 주소     저장된 포인터 값     가리키는 문자열
0x4747ffa8         0x4747ffda           "args-multiple"
0x4747ffb0         0x4747ffe8           "some"
0x4747ffb8         0x4747ffed           "arguments"
0x4747ffc0         0x4747fff7           "for"
0x4747ffc8         0x4747fffb           "you!"
0x4747ffd0         0x0                  NULL

보통 argv[0]은 프로그램 이름 또는 실행 경로다.

argv[0] = 프로그램 이름
argv[1]부터 = 사용자가 넘긴 인자
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment