안녕하세요 마무입니다. 이번 포스트에서는 "CPU 레지스터 종류"와 "CPU 레지스터 크기", "AH,AL", "rax, eax차이", "ax, ah, al"에 대해서 쉽고 자세히 설명해보려 합니다
-----목차-----
1. AT&T와 intel 어셈블리어 문법차이(%와 $가 붙는 경우)
2. gdb와 pwndbg 설치
3. 범용 레지스터는 CPU가 사용하는 변수다
i) A, B, C, D만 외우자(rax, eax, ax, ah, al)
ii) sp, bp는 포인터(주소)
iii) si, di도 그냥 범용 레지스터
iv) sp, bp, si, di는 8비트 단위가 없나?
4. EIP 레지스터
5. EFLAGS 레지스터
i) CF와 OF의 차이
6. R8, R9...R15 레지스터들
---------------
여기서 못 찾은 정보는
리눅스 독학 페이지 : https://mamu2830.blogspot.com/p/blog-page_13.html
네트워크 독학 페이지 : https://mamu2830.blogspot.com/p/blog-page_15.html
시스템 해킹 독학 페이지 : https://mamu2830.blogspot.com/p/blog-page_19.html
에서 찾아보면 있을 수 있습니다!
1. AT&T와 intel 어셈블리어 문법차이(%와 $가 붙는 경우)
지난 포스트에서 objdump로 봤던 어셈블리어에서 사실 이상함을 느낀 분들이 여럿 있을텐데요
바로 이 레지스터 앞에 '%'나 '$'가 붙는 모습이 우리가 아는 모습이 아니라는 거죠
사실 이런 %나 $가 붙는 어셈블리어는 'AT&T' 문법으로, 이 'AT&T'는 아는 사람은 아는 역사가 오래된 미국 회사입니다.
아마 기이함을 느꼈던 분들은 이 %나 $가 안 붙은 어셈블리어에 익숙했던 분들이실텐데요, 이 안 붙은 어셈블리어가 바로 우리에게 익순한 'intel'회사의 표준 문법입니다.
한번
objdump -M intel -D [프로그램]
이렇게 '-M intel'이란 옵션값을 주고 실행해보면
이렇게 우리에게 익숙하고 깔끔한 어셈블리어(intel)을 볼 수 있습니다.
당연히 intel 문법이 더 깔끔하기에 앞으로도 intel문법을 자주 사용할 것이며, 그냥 AT&T란 문법이란 것도 있구나 하고 넘어가면 됩니다~
참고로 '-M' 옵션은 아무리 찾아봐도 원래 영어 단어 뜻을 못 찾아서 저는 그냥 -M(Mode) intel 이렇게 '모드(Mode)로 외웠습니다.
2. gdb와 gef 설치
'objdump'는 기계어와 어셈블리어를 쉽게 볼 수 있는 편리한 도구이지만, 동적으로 메모리와 레지스터, 플래그 내 값이 변하는 것은 확인할 수가 없습니다.
당연히 이럴 때 사용할 수 있는 디버그 툴, 그 중에서 제일 리눅스에서 유명한 'gdb'를 사용해서 앞으로 디버깅을 할 텐데요
그냥 gdb만 사용하면 아무래도 실시간으로 레지스터와 플래그 변하는 것을 보기 힘들기에, 플러그인을 추가로 설치해서 좀 더 쉽게 볼 겁니다.
유명한 gdb 플러그인에는 peda, pwndbg, gef 등등 있는데요, 아쉽게도 pwndbg는 kali에서 공식적으로 지원을 안해서 저희는 'gef'를 사용할 겁니다.
꼭 칼리가 아니라 우분투를 사용하고 계신 분들이라면 pwndbg를 사용하셔도 됩니다!
우선 위 사이트에 들어가신 다음
스크롤을 좀 내리면 이렇게 'Instant Setup'이라는 부분에, 위 주황색 화살표로 표시한 여러개의 명령어들이 있습니다.
1. 위 명령어 중 아무거나 '하나' 복사 후
2. 리눅스 '터미널'에 붙여넣기 후
3. 엔터(실행)하면 자동으로 설치가 됩니다
긴 줄이 주르륵 뜨며 조금 걸려요~
이후 gdb [프로그램]을 실행했을 때, gdb 위치에 'gef'란 용어가 붙어있으면 성공적으로 설치가 된 겁니다!
이런 플러그인들은 말 그대로 기존 gdb에 기능이 더해진 것이므로, 기본 gdb 명령어들은 당연히 플러그인에 상관없이 실행되니, 너무 플러그인에 강박을 안 가지셔도 됩니다
3. 범용 레지스터는 CPU가 사용하는 변수다
일단 어셈블리어를 활용하려면 가장 기본적인 레지스터 종류는 어느정도 외워야 하는데요, 뭔가 레지스터 외우려니 벌써부터 어지러우실텐데, 솔직히 이걸 하나 말해주고 싶었습니다.
어렵게 생각할 것 없이 그냥 '레지스터'는 CPU가 사용하는 '변수'다 라는 것만 기억하면 된다고요!
왜냐면 레지스터들이 각 이름에 맞는 용도로만 쓰일 것 같지만, 사실 대부분은 거의 '범용' 레지스터라는 이름대로 그냥 기계어를 실행할 때 데이터를 임시적으로 저장할 때 쓰는 역할로 다 사용되기 때문이죠
이 CPU의 범용 레지스터가 사용되는 느낌은 프로그래밍 언어에서 임시 변수를 사용하는 것과 똑같습니다.
연산을 할 때 필요한 데이터들을 범용 레지스터에 넣은 뒤 계산을 하고, 그 계산 결과를 또 범용 레지스터에 저장하고, 메모리에 데이터를 옮기기 전 레지스터에 잠깐 저장해두고...
말 그대로 변수가 하는 일이죠?
그래도 당연히 가장 기본적인 이름들은 외워야하는데요, 그렇기에 제가 외우기 쉽게 요령을 알려드리겠습니다.
범용 레지스터는 대표적으로 8개가 있습니다만, 사실 이것도 아는 사람들은 아시다시피 rax, eax, ax, ah, al .. 이런식으로 확장형이 있어서 생각보다 외울게 많아 보입니다.
하지만 실상 정말 외워야할 것은 A, B, C, D 와 SI, DI, SP, BP면 된다는 것!!
i) A, B, C, D만 외우자(rax, eax, ax, ah, al)
자 알파벳 외울 때 순서 있죠? A B C D E F ... Z 이 느낌대로 "A, B, C, D"만 외우면됩니다.
A, B, C, D를 외우셨다면 그 다음엔 'X는 X(Hex, 16)'이란 것을 기억하시면 됩니다
'A'의 원래 영어 뜻은 A(Accumulator, 누산기)지만, 누산기??? 이름과 기능은 기억하지 마시고 딱 하나 위에서 말했듯이 A에 X(Hex, 16)가 붙었으니 AX는 16비트를 저장할 수 있다는 것만 기억하시면 됩니다.
똑같은 원리로 B(Base), C(Counter), D(Data) 모두 뜻은 기억하실 필요 없고, 똑같이 X(Hex, 16)이 붙었으니 BX, CX, DX도 16비트를 저장할 수 있다만 기억하시면 됩니다.
자 다시 정리하자면
AX, BX, CX, DX 모두 16비트 데이터를 저장할 수 있는 기억공간 단위!
그럼 EAX, EBX, ECX, EDX는 뭘까요? 이 E는 확장(Extend)이렇게 외우면 좋습니다.
그리고 컴퓨터는 항상 2배로 늘거나 줄죠? 그래서 확장된(E) AX, BX, CX, DX는 2배로 확장됐기에 각각 32비트 데이터를 저장할 수 있는 기억공간 단위입니다.
다시 정리하자면
EAX, EBX, ECX, EDX 모두 32비트 데이터를 저장할 수 있는 기억공간 단위!
그럼 R(AX,BX,CX,DX)의 R은 뭘까요? 아쉽게도 R은 원래 무슨 영어를 뜻하는 지 정확히 알려진 게 없습니다.. 하지만 저는 그냥 최신(Recent)이란 뜻의 R이라고 외웠습니다! 왜냐면요
현재 저희가 사용하는 대부분의 PC CPU는 64비트죠? 이 RAX, RBX, RCX, RDX도 똑같습니다! 바로 64비트 기억공간의 레지스터죠!
정리하면
E 다음인 최신(R)의 레지스터, RAX, RBX, RCX, RDX는 모두 64비트 데이터를 저장할 수 있는 기억공간 단위!
자 이번엔 낮은 단위를 외워보겠습니다
16비트(2바이트) AX, BX, CX, DX가 기준이라고 보시고, 먼저 AX를 예시로 들자면
00000000 00000000
당연히 위처럼 16비트가 있을겁니다. 하지만 제가 전 포스트에서 말했죠? 한 글자는 1바이트고 16진수를 사용해 두개의 16진수로 표현한다고!
그 중 앞에 빨간 8비트(1바이트, 1글자)는 높은(High)자리 비트라고 해서 AH라고 하며
뒤에 파랑 8비트(1바이트, 1글자)는 낮은(Low)자리 비트라고 해서 AL이라고 합니다.
어?? 느낌이 온다 그죠?
똑같은 원리로
BH, BL
CH, CL
DH, DL
모두 각각 8비트 단위에, 차이는 높은 8비트(H)냐 낮은 8비트(L)냐입니다!
축하합니다! 여러분은 벌써 RAX,RBX,RCX,RDX,EAX,EBX,ECX,EDX,AX,BX,CX,DX,AH,AL....
를 외웠습니다! ㅎㅎ
ii) si, di도 그냥 범용 레지스터
SI, DI라는 레지스터는 A, B, C, D 레지스터들이랑 이름 패턴이 약간 달라서 좀 당황스러우실텐데요
그냥 편하게 똑같이 A, B, C, D 와 같은 '범용' 레지스터라 생각하시면 됩니다
물!론! 엄연히 따지면 이 SI(Source Index)는 이름 그대로 시스템 콜을 이용해 데이터가 읽히거나 쓸 때 source(근원지 주소), DI(Destination Index)는 Destination(목적지 주소)를 가리키는 용도로도 사용되기도 하지만 대부분의 경우 A, B, C, D 처럼 범용으로 사용되기에
SI(Source index, 근원지 주소)와 DI(Destinatnion index, 목적지 주소)는 이름대로 쓰이지만, 범용(A, B, C, D)처럼 사용할 수도 있다~ 라 기억하시면 좋습니다.
자 그리고 SI와 DI도 범용 레지스터랬죠? AX, BX, CX, DX처럼
SI, DI는 16비트, E(Extend)가 붙은 ESI, EDI는 32비트, R(Recent)가 붙은 RSI, RDI는 64비트입니다.
iii) sp, bp는 포인터(주소)
자 우린 범용 레지스터 6개를 외웠습니다
그런데 사실 이 다음에 외울 SP(Stack Pointer)와 BP(Base Pointer)라는 것도 '범용 레지스터'에 묶여 있지만 그 느낌이 약간 달라서 따로 외우는 게 좋습니다
앞에 A, B, C, D는 진짜 '범용'이라서 그 레지스터 안에 '메모리 주소(포인터)'나 '특정 값'이 들어갈 수 있지만 이 SP(Stack Pointer)와 BP(Base Pointer)는 'P(Pointer, 메모리 주소)'라는 이름대로 그 안에 포인터(메모리 주소)만 저장합니다.
이 SP와 BP는 '메모리'를 관리할 때 매우 중요하게 사용하는 레지스터로, 나중에 해킹할 때 정말 중요하며 자주 보게 될 겁니다.
SP(Stack Pointer)는 이름 그대로 스택(Stack) 주소(Pointer)를 저장하는 레지스터라 이름이 그렇고
BP(Base Pointer)도 이름 그대로 스택의 밑바닥(Base) 주소(Pointer)를 저장하는 레지스터라 이름이 그런 겁니다.
구체적이고 자세한 내용은 스택 버퍼 오버플로우 부분에서 자세히 설명할 것이므로 여기서는 그냥 '포인터(메모리 주소)'를 저장할 때 쓰는 '레지스터'라고 외우면 됩니다!
자 그리고 AX, BX, CX, DX와 마찬가지로!
SP, BP는 각각 16 비트를 저장할 수 있는 기억 공간입니다.
그리고 확장(E)가 붙으면? 확장(2배)되니까!
ESP, EBP는 각각 32비트를 저장할 수 있는 기억공간
그리고 더 확장된 최신(R)은? 또 2배 확장됐을테니!!
RSP, RBP는 각각 64비트를 저장할 수 있는 기억공간
이렇게 되는거죠!!!
벌써 우린 8개의 레지스터를 외웠습니다!
iv) sp, bp, si, di는 8비트 단위가 없나?
혹시 SI, DI, SP, BP는 왜 AH, AL와 같은 8비트 공간 단위를 설명하지 않느냐 궁금하실 분들이 있을텐데요!
얘넨 8비트 단위가 없습니다 ㅎㅎ
4. EIP 레지스터란
위에서 다뤘던 6개의 범용 레지스터와 2개의 포인터 레지스터 외에도 익숙하게 보이는
IP(Instruction Pointer)라는 레지스터는, 이름 그대로 현재 CPU가 읽고(실행하고) 있는 명령 코드(Instruction)의 주소(Pointer)를 저장하고 있는 레지스터입니다.
명령이라 하니 헷갈릴 수 있는데, 그냥 실행중인 코드입니다.
예시를 보여드릴게요!
편하게 보기 위해 objdump로 뽑은, 지난 포스트에서 만든 'first'란 프로그램의 'main'함수의 기계어 + 어셈블리어입니다.
맨 왼쪽 상단 주황색 표시를 한 곳이 '메모리 주소'이며 정확히는
0x0000000000001139(16진수) 이고 그 오른쪽엔 기계어 "55"와 대응되는 어셈블리어 "push rbp"가 있죠?
프로그램이 시작하면 55 기계어(push rbp)가 실행될텐데, 이때 IP(Instruction Pointer)에 저장되는 값은 현재 실행되고 있는 명령(55, push rbp)의 메모리 주소(pointer)인 0x1139(16비트, 2바이트)라는 것이죠!
이처럼 IP(Instruction Pointer)는 현재 실행되고 있는 명령의 포인터(주소)를 저장하는 레지스터이며 다른 범용 레지스터처럼 16비트(2바이트)의 저장공간을 가진 기억공간입니다.
다른 범용 레지스터처럼...?
그렇죠! 똑같이
E붙인 EIP는 32비트의 저장공간
R붙인 RIP는 64비트의 저장공간
입니다!
5. EFLAGS 레지스터
EFLAGS 레지스터라고, CPU의 현재 상태를 표시할 때 사용하는 '플래그(Flag)'들을 가지고 있는 레지스터입니다.
사실 현대 64bit CPU는 64개의 플래그가 있지만 그 중 실제로 사용하는 플래그는 20개 정도라 하고,
공부할 땐 4개의 플래그만 알고 있으면 된다 생각합니다.
CF(Carry Flag) : 부호 없는 수의 연산 결과가 비트를 넘어가는 경우 설정되는 플래그입니다.
컴퓨터공학에서 'Carry'란 연산 결과 '받아 올림'을 의미합니다. 예를 들어 123459 + 34951 이란 것을 우리가 손으로 계산할 때 어떻게 하시나요?
암산으로 하는 사람도 있겠지만, 식이 복잡하다면 보통 1의 자리의 합이 10을 넘으면 10의 자리에 1을 추가하고, 10의 자리 합이 10 넘으면 100의 자리에 1을 추가하고...
이런 식으로 하잖아요? 이 때 더 큰 자리로 '1'을 넘길 때(받아 올림)의 이 '1'을 "Carry"라 합니다.
같은 원리로 '부호 없는 수의 연산 결과가 비트를 넘어가면' 당연히 이런 Carry가 생길테니, 이걸 표현한다 해서 'Carry Flag'라 합니다.
ZF(Zero Flag) : 연산 결과가 0(Zero) 일 경우 설정되는 플래그입니다.(설명 필요 없죠?)
SF(Sign Flag) : 연산 결과가 음수라서 표현(Sign)이 필요할 경우 설정되는 플래그입니다.
컴퓨터에서 연산 결과가 +(양수) 일 땐 아무것도 표시할 필요가 없지만, 연산 결과가 '-(음수)'일 때는 어떻게 해야할까요? 이렇듯 연산 결과가 음수일 때 표시(Sign)하는 플래그라 해서 SF(Sign Flag)라 한다!(라 전 외웠습니다)
OF(Overflow Flag) : 부호 있는 수의 연산 결과가 비트 범위를 넘을 경우(Overflow) 설정되는 플래그입니다.
i) CF와 OF의 차이
CF와 OF의 차이란, 원래 컴퓨터엔 '음수'라는 것이 없고 이것을 표현하기 위에 우린 '2의 보수'라는 것을 사용하는데요?
간단히 설명해서 음수를 표현하기 위해 최상위 비트를 '양'과 '음수'로 표현할 때 사용해서, 최상위 비트가 '1'이면 음수, '0'이면 양수로 하는 겁니다.
그리고 음수(2의 보수)와 양수를 더 했을 때 최상위 비트를 초과해서 합이 0이 돼야 해, 2의 보수(음수)를 만드는 방법은 양수를 NOT하고 + 1을 하는 겁니다.
총 8비트 연산이라고 쳤을 때
00000011(3)의 2의 보수 형태(음수)는 NOT하고 + 1한, 11111101(-3)입니다.
그럼 위 둘을 8비트 연산에서 더하면 8비트를 초과해서 00000000(0)이 되겠죠?
이처럼! '부호 있는 수'의 연산에선 최상위 비트를 '양수'와 '음수' 구분용으로 둬야하기 때문에 최대 사용 가능한 비트수가 1개가 적습니다.
자 그런데 만약 컴퓨터에서 '양수 + 양수'를 했더니 '음수'가 나오는 경우엔 어떻게 할까요?
연산 비트가 총 8비트일 때, 최상위 1비트를 빼면 총 표현 가능한 수는 -128~127이죠.
자 그럼 01000001(65) + 01000001(65)를 하면 결과가 10000010(-2)가 나오는 어이없는 일이 발생합니다. 그리고 이런 현상을 OverFlow(오버플로우)라고 하죠
이렇듯 양수 + 양수를 했더니 '음수'가 나오는 것 처럼 '부호 있는 연산 결과가 비트 범위를 넘는' 현상이 있을 때 CPU에서 표시하기 위해 사용하는 것이 OF(Overflow Flag)인 겁니다.
하지만! 부호가 없는 수를 연산할 땐 어떻게 할까요? 당연히 '양수'와' '음수' 구분이 필요 없기에 8비트 연산이라고 칠 때 표현가능한 숫자는 0~255입니다.
그런데 예를 들어 8비트 연산에서 10000000(128) + 10000001(129)이 일어나면 어떻게 해야할까요? 이처럼 연산 결과가 최상위 비트를 초과했다(실제론 64비트겠죠?)라는 것을 나타내기 위해 사용하는 것이 바로 '부호 없는 연산 결과가 비트 범위를 넘을 때 사용하는' CF(Carry Falg)라는 거죠!
6. R8, R9...R15 레지스터들
사실 현대에 64비트 CPU엔 위에서 말한 외워야하는 레지스터외에도 다양한 레지스터들이 있습니다. 대표적으로 R8~~~R15까지 있는 레지스터들이 있는데요.
그냥 위에서 외운 ABCD, SI,DI처럼 여러 용도로 사용되는 범용 레지스터다!
이렇게만 알고 넘어가시면 됩니다.
이번 포스트가 도움이 되셨다면 따뜻한 댓글 달기 및 팔로우 클릭을 해주시면
저의 포스트 퀄리티 향상에 도움을 줍니다 ㅎㅎ
그럼 다음에 더 좋은 포스트로 찾아뵙겠습니다!
댓글 없음:
댓글 쓰기
#1 여러분들이 소중한 시간을 투자해 달아주시는 따뜻한 댓글들은 저에게 정말 큰 힘이 됩니다!
#2 저의 각 포스트들은 엄청난 노력과 시간 투자를 통해 만들어진 포스트들로, 무단 복제나 모방하는 것을 금지합니다.
#3 저의 포스트에도 틀린 정보가 있을 수도 있습니다. 그럴 경우 친절한 말투로 근거와 함께 댓글로 달아주시면 정말 감사하겠습니다!