SONOTRI
  • Background - Computer Architecture
    2025년 02월 28일 22시 40분 18초에 업로드 된 글입니다.
    작성자: sonotree

    컴퓨터는 각자 다른 기능을 수행하는 여러 부품들(CPU, 저장장치, GPU 등)의 도움으로 작동한다. 각 부품들은 특징이 뚜렷하여 컴퓨터에서 고유의 기능을 수행한다.

     

    이처럼 서로 다른 부품들이 모여서 '컴퓨터'라는 하나의 기계로서 작동할 수 있는 것은 컴퓨터에 대한 기본 설계가 존재하기 때문이다. 이 설계에 맞춰 여러 HW가 개발되고, 이들을 조립해서 컴퓨터가 완성된다. 컴퓨터 과학에서는 이러한 설계를

    '컴퓨터 구조(Computer Architecture)'라고 부른다.

     

    전체적인 컴퓨터 구조 중에서 특히 CPU가 사용하는 명령어와 관련된 설계를 명령어 집합구조(Instruction Set Architecture, ISA)라고 하는데, 가장 널리 사용되는 ISA 중 하나가 바로 x86-64 아키텍처이다.

    시스템 해킹을 공부하는 시점에서 컴퓨터 구조 및 x86-64에 대한 이해는 앞으로의 학습에 중요한 기초 지식이 될 것이다. 이를 암기하려고 하기 보다는 큰 줄기를 살펴본다는 생각으로 가볍게 훑으면서 공부하면 좋을듯하다.

     

     


    01. 컴퓨터 구조

    컴퓨터 구조란, 컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법을 의미한다. 컴퓨터 구조는 (1)컴퓨터의 기능 구조에 대한 설계, (2)명령어 집합 구조,  (3)마이크로 아키텍처 그리고  (4)기타 하드웨어 및 컴퓨팅 방법(직접 메모리에 접근)에 대한 설계 등이 포함된다.

     

    "(1)컴퓨터 기능 구조에 대한 설계"란, 컴퓨터가 연산을 효율적으로 하기 위해 어떤 기능들이 컴퓨터에 필요한지 고민하고 설계하는 분야이다. 대표적으로 폰 노이만 구조, 하버드 구조가 있다.☆

     

    CPU의 명령어 대한 설계는 "(2)명령어 집합 구조(Instruction Set Architecture)"라고 불리며, CPU가 처리해야하는 명령어를 설계하는 분야이다. 대표적으로 ARM, MIPS, AVR, 인텔의 x86 및 x86-64 등이 있다.☆

     

    CPU의 하드웨어적 설계는 "(3)마이크로 아키텍처(Micro Architecture)"라고 불리며, 정의된 명령어 집합을 효율적으로 처리할 수 있도록 CPU의 회로를 설계하는 분야이다. 캐시 설계, 파이프라이닝, 슈퍼 스칼라, 분기 예측, 비순차적 명령어 처리 등이 있다.

     

    -> 본 강의에서는 현대적 컴퓨터의 기능 구조에 큰 영향을 끼쳤다고 평가받는 폰 노이만 구조와 명령어 집합 구조 중 x86-64 아키텍처에 대해 살펴볼것이다.

     

     

     


    02. 폰 노이만 구조

    초기 컴퓨터 과학자 중 한명인 폰 노이만은, 컴퓨터에 연산, 제어, 저장의 세 가지 핵심 기능이 필요하다고 생각했다.

    근대 컴퓨터는 연산과 제어를 위해 중앙처리장치(Central Processing Unit, CPU)를, 저장을 위해 기억장치(memory)를 사용한다. 그리고 장치간에 데이터나 제어 신호를 교환할 수 있도록 버스(bus)라는 전자 통로를 사용한다.

     

     

    <중앙처리장치(CPU)>

    CPU는 프로그램의 연산을 처리하고 시스템을 관리하는 컴퓨터의 두뇌이다. 

    프로세스의 코드를 불러오고, 실행하고, 결과를 저장하는 일련의 모든 과정이 CPU에서 일어난다. 아래의 것들로 구성된다.

     

    ALU(Arthmetic Logic Unit): 산술/논리 연산을 처리하는 산술논리장치

    레지스터(Register): CPU에 필요한 데이터를 저장한다                         

    ○ 제어장치: CPU를 제어한다                                                                  

     

     

    <기억장치(memory)>

    기억장치는 컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용되며, 용도에 따라 주기억장치와 보조기억장치로 분류된다. 주기억장치는 프로그램 실행과정에서 필요한 데이터들을 임시로 저장하기 위해 사용되며, 대표적으로 램(Random-Access Memory, RAM)이 있다. 이와 반대로 보조기억장치는 OS, 프로그램 등과 같은 데이터를 장기간 보관하고자 알 때 사용된다. 대표적으로 롬(Read Only Memory, ROM) 하드 드라이브(Hard Disk Drive, HDD), SSD(Solid State Drive)가 있다.

     

    작년에 저장 공간이 부족해서 SSD 카드를 새로 구입해 장착했었다. 그래서 내가 산 SSD의 정보를 알아보려고 한다.

    먼저 cmd를 열고 #diskpart 명령어를 입력하면 dispart가 실행된다.

     

    그리고 #list disk를 통해 살펴본 디스크가 몇번 디스크인지 확인하고 #sel disk [디스크 번호]를 입력해 살펴볼 디스크를 명시해준다. 나는 0번 디스크를 살펴봤고 여기서 SSD 카드가 SHPP41-1000GM인 것을 알 수 있었다. 내가 산 모델과 같은 것을 확인할 수 있다.

     

    그리고 현재 D드라이브로 사용하고 있는, 즉 용량이 부족하기 전까지 사용했던 SSD 카드 정보도 궁금해서 알아봤다. 

    HFS256GEJ9X101N 이거라고 한다.

     

     

    <버스(System bus)>

    버스는 컴퓨터의 구성요소를 서로 연결하고 데이터 전달을 위한 경로이다. 대표적인 버스의 종류들을 살펴보자.

     

    ○ 제어버스(Control Bus)

    : 제어 신호를 전달한다. Read와 Write 신호가 전달된다.

    -> 각 구성요소(CPU, Memory, I/O Unit)는 양방향으로 데이터 전달이 가능한 버스를 사용한다.

     

    주소버스(Address Bus)

    : 메모리의 주소나 I/O Unit의 포트 번호를 전달한다.

    -> CPU와 메모리는 단방향으로 데이터 전달이 가능한 버스를 이용함으로써 주소 전달은 CPU -> 메모리로만 가능하다

    -> CPU, 메모리는 I/O Unit과 양방향으로 데이터 전달이 가능한 버스를 사용한다.

     

      데이터 버스(Data Bus)

    : 데이터를 전달한다

    -> 각 구성요소는 양방향으로 데이터 전달이 가능한 버스를 사용한다.

     

     

     

    02.1. 기억장치가 있는데 CPU안에 레지스터가 필요한 이유?

    CPU는 굉장히 빠른 속도로 연산을 처리하는데, 이를 위해 데이터의 빠른 교환이 필요하다. 따라서 CPU는 필요한 데이터를 빠르게 공급하고, 반출할 수 있어야 자신의 효율을 제대로 발휘할 수 있다.

    하지만 CPU의 연산속도가 기억장치와의 데이터 교환속도보다 압도적으로 빠르기 때문에, 기억장치만을 사용하면 병목현상이 발생하게 된다. 따라서 CPU는 교환속도를 획기적으로 단축하기 위해 레지스터캐시라는 저장장치를 내부에 가진다.

     

     

     


    03. 명령어 집합 구조

    명령어 집합 구조(ISA)란, CPU가 해석하는 명령어의 집합을 의미한다. 프로그램은 기계어로 이루어져 있는데, 프로그램을 실행하면 이 명령어들을 CPU가 읽고 처리한다.

     

    ISA는 IA-32, x84-64, MIPS, AVR 등 다양하게 존재하며, 이렇게 다양한 ISA가 개발되고 사용되는 이유는 모든 컴퓨터가 동일한 수준의 연산 능력을 요구하지 않으며, 컴퓨팅 환경도 다르기 때문이다.

    예를 들어 x86-64는 고성능 프로세서를 설계하기 위해 사용되는데 때문에 이를 기반으로 한 CPU는 많은 전력을 소모하며 발열도 상대적으로 심하다. 그러므로 안정적으로 전력을 공급할 수 있고, 냉각 장치를 구비하는데 공간상의 부담이 크지 않은 데스크톱이나 랩톱에 적합하다. 그러나 드론이나 스마트폰 같은 크기가 작은 기기들은 이러한 제약조건을 해결하기 어려우므로 x86-64보다는 ARM이나 MIPS의 프로세스를 사용하는 것이 적합하다.

     

     

     


    04. x86-64 아키텍처

    04.1. n비트 아키텍처

    64bit, 32bit 아키텍처에서의 64와 32는 CPU가 한 번에 처리할 수 있는 데이터의 크기를 의미한다. 비트의 수가 클수록 데이터의 처리 속도나 용량(메모리) 등이 향상된다. 즉 64비트 컴퓨터는 64비트로 구성된 CPU를 사용하므로, 32비트 아키텍처보다 데이터 처리속도가 높아 한 번에 더 많은 데이터 처리가 가능하다.

     

    컴퓨터 과학에서는 64, 32를 CPU가 이해할 수 있는 데이터의 단위라는 의미에서 WORD라고 부른다. WORD의 크기는 CPU가 어떻게 설계됐느냐에 따라 달라진다.

     

     

     

    0.4.2. WORD가 크면 유리한 점

    현대의 PC는 대부분 64bit 아키텍처의 CPU를 사용하는데, 그 이유 중 하나는 가상메모리의 크기 차이 때문이다. 

    가상 메모리는 CPU가 프로세스에게 제공하는 가상 메모리 공간인데, 32비트 아키텍처에는 2의 32승(=4GB)가 최대로 제공 가능한  가상 메모리의 크기이다. 일상적으로 사용하기에는 무리가 없을 수 있지만, 많은 자원을 소모하는 전문 소프트웨어나 고사양의 게임을 하는 사람들에게는 부족할 수 있다.

     

    반면 64비트 아키텍처에서는 이론상 2의 64승(≒16EB)의 가상메모리를 제공할 수 있다. 이는 웬만해서는 완전한 사용이 불가능할 정도로 큰 크기이기 때문에, 가용한 메모리 자원이 부족해서 소프트웨어의 실행이 불가능한 상황은 거의 발생하지 않는다.

    바이트(Byte): 1Byte = 8bit
    킬로바이트(KB): 1KB = 1024Byte
    메가바이트(MB): 1MB = 1024KB
    기가바이트(GB): 1GB = 1024MB
    테라바이트(TB): 1TB = 1024GB
    페타바이트(PB): 1PB = 1024TB
    엑사바이트(EB): 1EB = 1024PB

     

     

     

    04.3. x86-64 아키텍처: 레지스터

    레지스터란, CPU의 빠른 데이터 처리를 돕기 위해 사용되는 임시저장공간으로 처리중인 데이터나 처리 결과가 담긴다.

    레지스터의 종류에는 범용 레지스터, 세그먼트 레지스터, 포인트 레지스터, 인덱스 레지스터, 플래그 레지스터가 있다. 레지스터의 종류를 살펴보자.

     

     

    <범용 레지스터>

    범용 레지스터는 연산 결과의 임시 저장, 산술 및 논리 연산, 주소 색인 등 다양한 용도로 사용되는 다목적 레지스터이다. 종류로는 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP, EIP가 있다. 

    이름 주용도
    EAX/RAX (accumulator Register, 누산기 레지스터) 산술, 논리 연산을 담당하는 레지스터로, 함수의 반환값이 이 레지스터에 저장된다.
    EBX/RBX (Base Register, 베이스 레지스터) 메모리 주소를 저장하기 위해 사용되는 레지스터이다. 종종 배열이나 문자열과 같은 데이터 구조에 접근하기 위한 기준 포인터로 사용된다.
    (x64에서는 주된 용도 없음)
    ECX/RCX(Count Register. 카운트 레지스터) 반복 작업에서 카운터 역할을 수행하는 레지스터이다. loop 명령어 사용시 레지스터의 값을 하나씩 감소시키며, 0이 될때까지 반복 잡업을 수행한다.
    EDX/RDX(Data Register, 데이터 레지스터) EAX 레지스터와 함께 사용하여 큰 수 연산을 하거나, 그 결과를 저장할 수 있는 레지스터이다. 
    (x64에서는 주된 용도 없음)

     

     

    <인덱스 레지스터>

    인덱스 레지스터는 메모리 내의 데이터 접근 및 조작에 특화된 레지스터이다.

    -> SI와 DI는 x86 아키텍쳐에서 범용 레지스터로 분류되기도 한다.

    이름 주용도
    ESI/RSI(Source Index, 소스 인덱스 레지스터) 데이터 복사, 문자열 연산, 입출력 처리 등의 작업에서 소스 데이터이 주소를 가리키는데 사용된다. 간단히 말해, 데이터를 옮길 때 원본을 가리키는 포인터이다.
    EDI/RDI(Destination Index, 목적지 인덱스 레지스터) 데이터 복사, 문자열 처리, 배열 조작 등의 작업에서 목적지 데이터이 메모리 주소를 가리키는데 사용된다. 간단히 말해, 데이터를 옮길 떄 목적지를 가리키는 포인터이다.
    RSP(Stack Pointer) 사용중인 스택의 위치를 가리키는 포인터
    RBP(Stack Base Pointer) 스택 바닥을 가리키는 포인터

     

     

     

    <세그먼트 레지스터>

    메모리를 다른 세그먼트(segment)로 나누어 관리하고, 각 세그먼트에 대한 기준 주소를 저장한다. 세그먼트를 이용함으로써, 물리 메모리를 효율적으로 사용하고 프로그램간 메모리 격리를 가능하게 한다.

     

    또한 세그먼트 레지스터는 x64로 아키텍처가 확장되며 용도에 큰 변화가 생긴 레지스터이기도 하다. 과거에는 세그먼트 레지스터를 이용하여 사용 가능한 물리 메모리의 크기를 키우려고 했지만, x64를 사용하는 현재는 사용 가능한 주소 영역이 굉장히 넓기 때문에 이런 용도로는 거의 사용되지 않는다.

     

    현대의 x64에서 cs, ds, ss 레지스터는 코드 영역, 데이터 영역, 스텍 메모리 영역을 가리킬 때 사용되고 나머지 레지스터는 OS별로 용도를 결정할 수 있도록 범용적인 용도로 제작된 세그먼트 레지스터이다.

    이름 주용도
    cs(Code Segment, 코드 세그먼트) 현재 프로그램의 코드가 포함된 세그먼트의 주소를 저장하는 레지스터
    ds(Data Segment, 데이터 세그먼트) 데이터가 포함된 세그먼트의 주소를 저장하는 레지스터
    ss(Stack Segment, 스택 세그먼트) 스텍이 포함된 세그먼트의 주소를 저장하는 레지스터
    es, fs, gs 추가적인 데이터 <-> 세그먼트 주소를 저장하는 레지스터

     

     

     

    <(명령어)포인터 레지스터>

    프로그램은 일련의 기계어 코드로 이루어져있다. 이 중에서 CPU가 어느 부분의 코드를 실행할지 가리키는것이 바로 명령어 포인터 레지스터의 역할이다. x64 아키텍처의 명령어 레지스터는 RIP이며, 크기는 8Byte이다.

    이름 주용도
    EIP/RIP(Instruction Pointer, 명령 포인터) 다음에 실행될 명령의 메모리 주소를 저장한다.
    ESP/RSP(Stack Pointer, 스택 포인터 레지스터) 프로그램의 스택 메모리 내에서, 현재 스택 최상단 주소를 저장하는 레지스터이다. 함수 호출, 지역 변수 관리, 함수 내 데이터 저장 및 복구 등의 작업에서 필수적으로 사용된다.
    EBP/RBP(Base Pointer, 베이스 포인터 레지스터) 함수내이 지역 변수와 인자에 일관되고 쉽게 접근하기 위해 사용되는 포인터 역할 레지스터이다. 스택 내에서 접근할 부분의 메모리 주소를 저장한다.

     

     

     

    <플래그 레지스터>

    플래그 레지스터는 프로세서의 현재 상태를 저장하고 있는 레지스터이다. x64 아키텍처에서는 RFLAGS라고 불리는 64비트 크기의 플래그 레지스터가 존재하며, 과거 16비트 플래그 레지스터가 확장된 것이다. 깃발을 올리고, 내리는 행위로 신호를 전달하듯 플래그 레지스터는 자신을 구성하는 여러 비트들로 CPU의 현재 상태를 표현한다.

     

    RFLAGS는 64비트이므로 최대 64개의 플래그를 사용할 수 있지만, 실제로 20여개의 비트만 사용한다. 

    이름 목적
    ZF(Zero Flag) 목적: 작업 결과가 0일 경우 설정한다
    SF(Sign Flag) 목적: 작업 결과가 음수일 경우 설정한다
    사용: 부호화된 산술 연산에서 결과의 부호를 나타낸다
    CF(Carry FLAG) 목적: 산술 연산에서 가장 중요한 비트의 수행이 발생할 경우 설정한다(부호가 없는 연산에서 유용하다)
    사용: 최소 비트에서 다음 비트로의 오버플로를 나타내기 위해 다중 정밀도 산술에서 사용된다.
    OF(Overflow FLAG) 목적: 산술 연산으로 인해 부호화된 오버플로가 발생할 경우 설정한다. 즉 결과가 너무 커서 지정된 비트 수로 표시할 수 없을 경우 설정된다.
    사용: 서명된 산술 연산에서 중요하다.
    예를 들어, a=3 그리고 b=5일 때 a-b 연산을 하면 연산의 결과가 음수이므로 SF가 설정된다.
    -> 그러면 CPU는 SF를 통해 a가 b보다 작았음을 알 수 있게된다.

     

     

     


    05. 마무리

    • 범용 레지스터(General Register): 주 용도는 있으나, 그 외의 용도로도 자유롭게 사용할 수 있는 레지스터. 
    • 세그먼트 레지스터(Segment Register): 과거에는 메모리 세그멘테이션이나, 가용 메모리 공간의 확장을 위해 사용됐으나 현재는 주로 메모리 보호를 위해 사용되는 레지스터이다.
    • 플래그 레지스터(Flag Register): CPU의 상태를 저장하는 레지스터
    • 명령어 포인터 레지스터(Instruction Pointer Register, IP): CPU가 실행해야할 코드를 가리키는 레지스터. x64에는 RIP가 있다.

    'Dreamhack > System Hacking' 카테고리의 다른 글

    System Hacking Introduction-Tool: Environment Setip  (0) 2025.02.27
    댓글