Background: Computer Architecture
# 서론
- 컴퓨터는 각자 다른 기능을 수행하는 여러 부품들의 도움으로 작동
- CPU: 컴퓨터 작동에 핵심이 되는 연산을 처리하고, 저장 장치는 데이터를 저장
- 이외에도 GPU는 그래픽 데이터를, 랜카드는 네트워크 통신을, 사운드 카드는 소리 데이터를 처리하는 것에 특화되어 있음
- 각 부품들은 특징이 뚜렷하여 컴퓨터에서 고유의 기능을 수행함
- 서로 다른 부품들이 모여서 '컴퓨터'라는 하나의 기계로서 작동할 수 있는 것은 컴퓨터에 대한 기본 설계가 존재하기 때문
> 설계에 맞춰서 여러 하드웨어가 개발되고, 이들을 조립해서 컴퓨터가 완성됨
> 컴퓨터 과학에서는 이러한 설계를 '컴퓨터 구조(Computer Architecture)'라고 부름
- 명령어 집합구조(Instruction Set Architecture, ISA): 전체적인 컴퓨터 구조 중에서 특히 CPU가 사용하는 명령어와 관련된 설계
> 가장 널리 사용되는 ISA: Intel의 x86-64 아키텍처
- 개요
- 컴퓨터 구조(Computer Architecture)
- 명령어 집합 구조(Instruction Set Architecture, ISA)
- 범용 레지스터(General Register)
- 세그먼트 레지스터(Segment Register)
- 플래그 레지스터(Flag Register)
- 명령어 포인터 레지스터(Instruction Pointer Register, IP)
# 컴퓨터 구조와 명령어 집합 구조
1. 컴퓨터 구조 💻
- 컴퓨터 구조(Computer Architecture): 컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법을 말함
- 컴퓨터 구조는 컴퓨터의 기능 구조에 대한 설계, 명령어 집합구조, 마이크로 아키텍처, 그리고 기타 하드웨어 및 컴퓨팅 방법에 대한 설계 등이 포함
- '컴퓨터의 기능 구조에 대한 설계': 컴퓨터가 연산을 효율적으로 하기 위해 어떤 기능들이 컴퓨터에 필요한지 고민하고, 설계하는 분야
ex) 폰 노이만 구조, 하버드 구조, 수정된 하버드 구조
- 명령어 집합구조(Instruction Set Architecture): CPU의 명령어에 대한 설계(CPU가 처리해야하는 명령어를 설계하는 분야)
ex) ARM, MIPS, AVR, 인텔의 x86 및 x86-64 등이 있음
- 마이크로 아키텍처(Micro Architecture): CPU의 하드웨어적 설계(정의된 명령어 집합을 효율적으로 처리할 수 있도록, CPU의 회로를 설계하는 분야)
2. 폰 노이만 구조
- 폰 노이만: 컴퓨터에 연산, 제어, 저장의 세 가지 핵심 기능이 필요하다고 생각함
- 근대의 컴퓨터는 연산과 제어를 위해 중앙처리장치(Central Processing Unit, CPU)를, 저장을 위해 기억장치(Memory)를 사용
- 장치간에 데이터나 제어 신호를 교환할 수 있도록 버스(bus)라는 전자 통로를 사용함
중앙처리장치 🧠
- CPU는 프로그램의 연산을 처리하고 시스템을 관리하는 컴퓨터의 두뇌
- 프로세스의 코드를 불러오고, 실행하고, 결과를 저장하는 일련의 모든 과정이 CPU에서 일어남
- CPU 구성
> 산술 논리 장치(Arithmetic Logic Unit, ALU): 산술/논리 연산을 처리
> 제어 장치(Control): CPU를 제어하는 장치
> 레지스터(Resister): CPU에 필요한 데이터를 저장함
기억장치 💾
- 기억장치는 컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용되며, 용도에 따라 주기억장치와 보조기억장치로 분류
- 주기억장치는 프로그램 실행과정에서 필요한 데이터들을 임시로 저장하기 위해 사용
ex) 램(Random-Access Memory, RAM)
- 보조기억장치는 운영체제, 프로그램 등과 같은 데이터를 장기간 보관하고자 할 때 사용
ex) 하드 드라이브(Hard Disk Drive, HDD), SSD(Solid State Drive)
버스 🚌
- 버스는 컴퓨터 부품과 부품 사이 또는 컴퓨터와 컴퓨터 사이에 신호를 전송하는 통로
> 데이터 버스(Data Bus): 데이터가 이동
> 주소 버스(Address Bus): 주소를 지정
> 제어 버스(Control Bus): 읽기/쓰기를 제어
- 위의 3가지 외에도 랜선이나 데이터 전송 소프트웨어, 프로토콜 등도 버스라고 불림
💡 기억장치가 있는데 CPU안에 레지스터가 왜 필요한가요 ?
- CPU는 굉장히 빠른 속도로 연산을 처리하는데, 이를 위해 데이터의 빠른 교환이 필요함
- 예를 들어 사탕을 1초에 100개 생산하는 기계가 있다고 가정. 만약 이 기계에 초당 100개의 재료를 공급하지 못한다면, 재료가 공급될 때까지 대가해야 하므로 최대의 생산효율을 달성할 수 없음. 또한 완성된 사탕을 초당 100개씩 가져가지 못한다면, 결국 기계 앞에 사탕이 쌓여서 생산을 중단해야 하는 상황을 맞게 됨
- 위의 예시와 마찬가지로 CPU도 필요한 데이터를 빠르게 공급하고, 반출할 수 있어야 자신의 효율을 제대로 발휘할 수 있음
- 그런데 CPU의 연산 속도가 기억장치와 데이터 교환 속도보다 압도적으로 빠르기 때문에, 기억 장치만을 사용하면 병목현상이 발생할 수 있음
- 따라서 CPU는 교환속도를 획기적으로 단축하기 위해 레지스터와 캐시라는 저장장치를 내부에 갖고 있음
3. 명령어 집합 구조
- 명령어 집합 구조(Instruction Set Archtecture, ISA)란 CPU가 해석하는 명령어의 집합을 의미
- 프로그램은 기계어로 이루어져 있는데, 프로그램을 실행하면 이 명령어들을 CPU가 읽고, 처리
- ISA는 IA-32, x86-64(x64), MIPS, AVR 등 다양하게 존재
- 다양한 ISA가 개발되고 사용되는 이유는 모든 컴퓨터가 동일한 수준의 연산 능력을 요구하지 않으며, 컴퓨팅 환경도 다양하기 때문
- 예를 들어, 인텔의 x86-64는 고성능 프로세서를 설계하기 위해 사용됨
> 이를 기반으로한 CPU들은 많은 전력을 소모하며, 발열도 상대적으로 심함
> 그러므로 안정적으로 전력을 공급할 수 있고, 냉각 장치를 구비하느데 공간상의 부담이 크지 않은 데스크톱 또는 랩톱에 적합함
- 드론과 같은 배터리를 사용하거나 공유기, 인공지능 스피커처럼 크기가 작은 임베디드 기기들은 위와 같은 제약조건을 해결하기 어려움
> 특히 스마트폰은 피부에 닿기 때문에 발열 문제에 민감하고, 배터리로 작동하므로 인텔의 고성능 프로세서를 장착하기 매우 부적합함
> 그래서 많은 임베디드 장비들은 전력 소모와 발열이 적은 ARM이나 MIPS 또는 AVR의 프로세서를 사용하고 있음
# x86-64 아키텍처
1. x86-64 아키텍처
- x64 아키텍처는 인텔의 64비트 CPU 아키텍처임
- 인텔의 32비트 CPU 아키텍처인 IA-32를 64비트 환경에서 사용할 수 있도록 확장한 것으로, 대다수의 개인용 컴퓨터들이 인텔의 x64 CPU를 사용하고 있음
n 비트 아키텍처
- 위의 '64비트 아키텍처', '32비트 아키텍처'에서 64와 32는 CPU가 한번에 처리할 수 있는 데이터 크기
- 컴퓨터과학에서는 이를 CPU가 이해할 수 있는 데이터의 단위라는 의미에서 WORD라고 부름
- WORD의 크기는 CPU가 어떻게 설계됐느냐에 따라 달라짐
ex) 일반적인 32비트 아키텍처에서 ALU는 32비트까지 계산할 수 있으며, 레지스터의 용량 및 각종 버스들의 대역폭이 32비트임.
따라서 이들로 구성된 CPU는 설계 상 32비트의 데이터까지만 처리할 수 있게 됨
WORD가 크면 유리한 점
- 현대의 PC는 대부분 64비트 아키텍처의 CPU를 사용하는데, 그 이유 중 하나는 32비트 아키텍처의 CPU가 제공할 수 있는 가상메모리의 크기가 작기 때문임
- 가상메모리는 CPU가 프로세스에게 제공하는 가상의 메모리 공간인데, 32비트 아키텍처에서는 4GB가 최대로 제공 가능한 가상 메모리의 크기임
- 일상적으로 사용하기에는 적절할 수 있지만, 많은 메모리 자원을 소모하는 전문 소프트웨어나 고사양의 게임 등을 실행할 때는 부족할 수 있음
- 반면 64비트 아키텍처에서는 이론상 16EB(Exabyte)의 가상메모리를 제공할 수 있음
- 이는 웬만해서는 완전한 사용이 불가능할 정도로 큰 크기이기 때문에, 가용한 메모리 자원이 부족해서 소프트웨어의 최고 성능을 낼 수 없다거나 소프트웨어의 실행이 불가능한 상황은 거의 발생하지 않음
💡 x86-64의 여러 이름: Intel64, IA-32e, amd64, EM64T, ...
2. x86-64 아키텍처: 레지스터
- 레지스터는 CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소이며, 산술 연산에 필요한 데이터를 저장하거나 주소를 저장하고 참조하는 등 다양한 용도로 사용됨
- x64 아키텍처에는 다음과 같은 레지스터들이 존재
범용 레지스터 🧺
- 범용 레지스터는 주용도는 있으나, 그 외의 다양한 용도로 사용될 수 있는 레지스터
- x86-64에서 각각의 범용 레지스터는 8바이트를 저장할 수 있으며, 부호 없는 정수를 기준으로 2^64-1까지의 수를 나타낼 수 있음
- 자주 사용되는 범용 레지스터들의 주용도는 다음과 같음
> 이 외에도 x64에는 r8, r9, ..., r15 까지의 범용레지스터가 더 존재
이름 | 주용도 |
rax (accumulator register) | 함수의 반환 값 |
rbx (base pointer) | x64에서는 주된 용도 없음 |
rcx (counter register) | 반복문의 반복 횟수, 각종 연산의 시행 횟수 |
rdx (data register) | x64에서는 주된 용도 없음 |
rsi (source index) | 데이터를 옮길 때 원본을 가리키는 포인터 |
rdi (destination index) | 데이터를 옮길 때 목적지를 가리키는 포인터 |
rsp (stack pointer) | 사용 중인 스택의 위치를 가리키는 포인터 |
rbp (stack base pointer) | 스택의 바닥을 가리키는 포인터 |
세그먼트 레지스터 🗂️
- x64 아키텍처에는 cs, ss, ds, es, fs, gs 총 6가지의 세그먼트 레지스터가 존재하며, 각 레지스터의 크기는 16비트임
- 세그먼트 레지스터는 x64로 아키텍처가 확장되면서 용도에 큰 변화가 생긴 레지스터
- 과거 IA-32, IA-16에서는 세그먼트 레지스터를 이용하여 사용 가능한 물리 메모리의 크기를 키우려고 했음
ex) IA-16에서는, 어떤 주소를 cs:offset라고 한다면, 실제로는 cs<<4+offset의 주소를 사용하여 16비트 범위에서
접근할 수 없는 주소에 접근할 수 있었음
- 당시에는 범용 레지스터의 크기가 작아서 사용 가능한 메모리의 주소 폭이 좁았지만, x64에서는 사용 가능한 주소 영역이 굉장히 넓기 때문에 위와 같은 용도로는 거의 사용되지 않음
- 현대의 x64에서 cs, ds, ss 레지스터는 코드 영역과 데이터, 스택 메모리 영역을 가리킬 때 사용되고, 나머지 레지스터는 운영체제 별로 용도를 결정할 수 있도록 범용적인 용도로 제작된 세그먼트 레지스터임
명령어 포인터 레지스터 👇
- 프로그램은 일련의 기계어 코드들로 이루어져 있음
- 이 중에서 CPU가 어느 부분의 코드를 실행할지 가리키는게 명령어 포인터 레지스터의 역할
- x64 아키텍처의 명령어 레지스터는 rip이며, 크기는 8바이트임
플래그 레지스터 🏳️🏴
- 플래그 레지스터는 프로세서의 현재 상태를 저장하고 있는 레지스터
- x64 아키텍처에서는 RFLAGS라고 불리는 64비트 크기의 플래그 레지스터가 존재하며, 과거 16비트 플래그 레지스터가 확장된 것
- 깃발을 올리고, 내리는 행위로 신호를 전달하듯, 플래그 레지스터는 자신을 구성하는 여러 비트들로 CPU의 현재 상태를 표현
- RFLAGS는 64비트이므로 최대 64개의 플래그를 사용할 수 있지만, 실제로는 아래의 20여개의 비트만을 사용
- 이 중에서도 앞으로 주로 접하게 될 FLAG들은 다음과 같음
FLAG | 의미 |
CF(Carry Flag) | 부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우 설정됨 |
ZF(Zero Flag) | 연산의 결과가 0일 경우 설정됨 |
SF (Sign Flag) | 연산의 결과가 음수일 경우 설정됨 |
OF (Overflow Flag) | 부호 있는 수의 연산 결과가 비트 범위를 넘을 경우 설정됨 |
- 플래그를 사용하는 간단한 예
ex) 3의 값을 갖는 a와 5의 값을 갖는 b가 있을 때, a에서 b를 빼는 연산을 하면, 연산의 결과가 음수이므로 SF가 설정됨
그러면 CPU는 SF를 통해 a가 b보다 작았음을 알 수 있음
레지스터 호환
- x86-64 아키텍처는 IA-32의 64비트 확장 아키텍처이며, 호환이 가능함
- IA-32에서 CPU 레지스터들은 32비트 크기를 가지며, 이들의 명칭은 각각 eax, ebx, ecx, edx, esi, edi, esp, ebp 였음
- 호환성을 위해 이 레지스터들은 x86-64에서도 그대로 사용이 가능함
- 앞서 소개한 rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp가 이들의 확장된 형태이며, eax, ebx 등은 확장된 레지스터의 하위 32비트를 가리킴
ex) eax는 rax의 하위 32비트를 의미
- 과거 16비트 아키텍처인 IA-16과의 호환을 위해 ax, bx, cx, dx, si, di, sp, bp는 eax, ebx, ecx, edx, esi, edi, esp, ebp의 하위 16비트를 가짐
> 이들 중 몇몇은 다시 상위 8비트, 하위 8비트로 나뉘는데 이들 전체에 대한 내용은 아래 그림에서 확인 가능
# 마무리
- 코스 요약 🗒️
- 범용 레지스터(General Register): 주 용도는 있으나, 그외의 용도로도 자유롭게 사용할 수 있는 레지스터. x64에는 rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp, r8-r15가 있음
- 세그먼트 레지스터(Segment Register): 과거에는 메모리 세그멘테이션이나, 가용 메모리 공간의 확장을 위해 사용됐으나, 현재는 주로 메모리 보호를 위해 사용되는 레지스터 x64에는 cs, ss, ds, es, fs, gs가 있음
- 플래그 레지스터(Flag Register): CPU의 상태를 저장하는 레지스터
- 명령어 포인터 레지스터(Instruction Pointer Register, IP): CPU가 실행해야 할 코드를 가리키는 레지스터, x64에는 rip가 있음
# Quiz
풀이: ah와 al은 각각 16비트 ax의 왼쪽 부분과 오른쪽 부분을 구성하기 때문에 답은 C가 됨
풀이: al은 ax의 오른쪽 부분을 구성하기 때문에 답은 A가 됨
풀이: eax는 rax의 하위 32비트를 의미하므로 답은 B가 됨
풀이: ah는 ax의 왼쪽 부분을 구성하기 때문에 답은 C가 됨
풀이: ZF 플래그는 연산의 결과가 0일 경우에 설정되므로 rax와 rbx의 대소관계는 ==임을 알 수 있으므로
답은 C가 됨
'Hacking Tech > System Hacking' 카테고리의 다른 글
[Dreamhack] Tool: pwntools (0) | 2022.03.28 |
---|---|
[Dreamhack] Tool: gdb (0) | 2022.03.27 |
[Dreamhack] x86 Assembly🤖: Essential Part(2) (0) | 2022.03.27 |
[Dreamhack] x86 Assembly🤖: Essential Part(1) (0) | 2022.03.26 |
[Dreamhack] Background: Linux Memory Layout (0) | 2022.03.25 |