개요
- 이 stage 2에서는 컴퓨터 구조와 명령어 집합구조, 그리고 인텔의 x86-64에 대해 살펴본다고 한다.
- 또한, 운영체제 중 하나인 linux의 memory의 layout에 대해 알아본다고 한다.
- 여기서 배울 것들?
- 컴퓨터 구조(Computer Architecture)
- 명령어 집합 구조(Instruction Set Architecture, ISA)
- 범용 레지스터(General Register)
- 세그먼트 레지스터(Segment Register)
- 플래그 레지스터(Flag Register)
- 명령어 포인터 레지스터(Instruction Poitner Register, IP)
컴퓨터 구조
- 컴퓨터 구조(Computer Architecture?)
- 대충 컴퓨터라는 하나의 기계로써, 작동할 수 있게끔 설계해놓는 그러한것?
- 즉, 컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고 구성하는 방법.
- 명령어 집합구조(Instruction Set Architecture, ISA)
- CPU의 명령어에 대한 설계를 하는 것.
- = CPU가 처리행하는 명령어를 설꼐하는 분야.
- ex) ARM, MIPS, AVR, 인텔의 X86 / x86-64
- 마이크로 아키텍처(Micro Architecture)?
- CPU의 ‘하드웨어’적 설계에 대해 정의하는 말로써, 정의된 명령어 집합을 효율적으로 처리할 수 있도록, CPU의 회로를 설계하는 분야라고 합니다.
컴퓨터 구조의 세부 분야
- 기능 구조의 설계
- 폰 노이만 구조
- 지금 현재 제일 보편화된 구조.
- 컴퓨터에 ‘연산’,’제어’, ‘저장’의 세가지 핵심 기능이 필요하다고 생각하였고, 결국 그렇게 만들어졌다고 합니다.
- 연산, 제어?
- 이건 CPU가 담당한다고 합니다.
- 저장?
- 이건 기억장치(memory)를 사용합니다.
- etc?
- 이제 저 서로간의 신호를 교환하기 위해 버스(bus)라는 전자통로를 사용한다고 합니다.
- 연산, 제어?
- CPU?
- CPU는 프로그램의 연산을 처리하고 시스템을 관리하는 컴퓨터의 두뇌라고 합니다.
- 여기서 산술/논리 연산을 처리하는 ‘산술논리장치(ALU)와 CPU를 제어하는 제어장치(Control unit), CPU에 필요한 ‘데이터’를 저장하는 레지스터(Register)등으로 구성됩니다.
- 기억장치(memory)?
- 기억장치는 컴퓨터가 동작하는데 필요한 여러데이터를 저장하기 위해 사용된다고 합니다.
- 메모리의 용도에 따라 기억장치의 종류가 달라진다고 합니다.
- 주기억장치
- 주기억장치는 프로그램 실해오가정에서 필요한 데이터를 임시로 저장하기 위해 사용되며, 대표적으로 램(Random-Access Memory, RAM)이 있습니다.
- 보조기억장치.
- 운영체제, 프로그램 등과 같은 데이터를 ‘장기간’ 보관하고자 할 때 사용된다 합니다.
- ex) SSD, HDD, Flash drive
- 운영체제, 프로그램 등과 같은 데이터를 ‘장기간’ 보관하고자 할 때 사용된다 합니다.
- 주기억장치
- 버스
- 컴퓨터 부품과 부품 사이 or 컴퓨터 - 컴퓨터 사이에 신호를 전송하는 ‘통로’를 말한다.
- 종류는 아래와 같다.
- 데이터 버스(Data bus)
- 주소 버스(Address Bus)
- 제어 버스(control bus)
- 랜선(ethernet cable)
- 데이터 전송 소프트웨어
- 프로토콜(protocol)
- 하버드 구조
- ‘수정된’ 하버드 구조
- 폰 노이만 구조
- 명령어 집합 구조(Instruction Set Architecture, ISA)
- - 명령어 집합 구조는 CPU가 해석하는 명령어의 집합을 의미한다. = 어셈블리어의 종류.
N 비트 아키텍처와 같이 비트단위로 하는듯..
- 종류는 아래와 같다.
- x86, x86-64
- 발열 심함, 대신 빠름, 안정적으로 전력을 공급할 수 있음.
- 고성능 프로세서를 설계하기 위해 사용됨.
- 애초에 intel x86-64자체가 고성능 프로세서를 설계하기 위해 사용된 이유로는, 컴퓨터 과학 자체에서 CPU가 한번에 처리할 수 있는 데이터 크기를 Word라고 하는데 그 word가 크면 클 수록 가상메모리의 크기가 커짐.
- 근데 그냥 x86은 32bit으로써, 4GB정도가 최대한으로 제공할 수 있는 가상 메모리라고 한다.
- 하지만, 이 x86-64는 64bit로써, 16 엑사 바이트 (16,777,216테라바이트)정도 까지 커버 가능하면서 제공한다고 한다.
- → 하지만, 하드웨어적으로는 아직까진 불가능…
- x86-64 아키텍처의
- ARM
- 발열 없고, 가볍게 돌릴만한 프로세서. 아래 두개도 마찬가지.
- MIPS
- AVR
- x86, x86-64
- 마이크로 아키텍처
- 캐시 설계
- 파이프라이닝(pipering?)
- 슈퍼 스칼라
- 분기 예측
- 비순차적 명령어 처리
- 하드웨어 및 컴퓨팅 방법론
- 직접 메모리 접근.
x86-64 아키텍처! : 레지스터 편
- 이제 엄청 보편화가 된 64bit 기반 아키텍처인 x86-64(a.k.a. AMD64)의 레지스터가 어떻게 이뤄져있고, 각 레지스터의 종류를 알아보는 시간을 가지는 시간이 되겠다.
- 일단 먼저 ‘레지스터’가 뭔지 알아보는 시간을 먼저 가져보자.
→레지스터?
- CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소이며, 산술 연산에 필요한 데이터를 저장하거나 주소를 저장하고 참조하는 등의 다양한 용도로 사용된다고 합니다.
→레지스터의 종류?
- 범용 레지스터(64bit 기반 아키텍처 기준)
- 범용 레지스터는 8byte를 저장할 수 있다.
- 부호 없는 정수를 기준으로 2^64 -1 까지의 수를 나타낼 수 있다고 한다.
- 자주 쓰이는 레지스터.
- rax (accumulator register)
- 함수의 반환 값.
- rbx (base register)
- x64에서는 주된 용도 없음….(그러면 원래 용도는..?)
- rcx (counter register)
- 반복문의 반복 횟수, 각종 연산의 시행 횟수.
- rdx (data register)
- x64에서는 주된 용도가 없음.
- rsi (source index)
- 데이터를 옮길 때 원본을 가리키는 포인터.
- rdi (destination index)
- 데이터를 옮길 때 목적지를 가리키는 포인터.
- rsp (stack pointer)
- 사용중인 스택의 위치를 가리키는 포인터.
- rbp (stack base pointer)
- Stack의 바닥을 가리키는 포인터.
- rax (accumulator register)
- 세그먼트 레지스터.(64bit 아키텍처 기준)
- cs
- ss
- ds
- es
- fs
- gs
- 각 레지스터의 크기는 16비트(4byte)다.
- 명령어 포인터 레지스터
- CPU가 어느 부분의 코드를 실행할지 가리키는 역할을 수행함.
- RIP
- 8byte 크기를 가진 해당 레지스터.
플레그 레지스터(x86-64기준)
- 플래그 레지스터는 프로세서의 현재 상태를 저장하고 있는 레지스터다.
- 여기선 RFLAGS라고 불리는 64bit 크기의 플래그 레지스터가 존재하며, 과거 16비트 플래그 레지스터가 확장된 형태라고 한다.
- 깃발을 올리고, 내리는 행위로 신호를 전달하듯, 플래그 레지스터는 자신을 구성하는 여러 비트들로 CPU의 현재상태를 표현한다.
위의 그림은 RFLAGS를 나타내주는 그림으로써, 실제 64bit지만 20여개의 bit를 사용하는 것을 알 수 있다. 그 중 우리 해커들이 봐야할 것은 다음과 같았다.
- CF (Carry Flag)
- 부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우 설정 된다.
- ZF (Zero Flag)
- 연산의 결과가 0일 경우 설정 된다.
- SF (Sign Flag)
- 연산의 결과가 음수일 경우 설정 된다.
- OF (Oveflow Flag)
- 부호 있는 수의 연산 결과가 비트 범위를 넘을 경우 설정된다.
해당되는 플래그를 예시로 든것은 다음과 같았다고 한다.
- a = 3
- b = 5
- a - b를 하게 되면?
- 연산의 결과 = 음수. == SF (Sign Flag)가 설정된다.
- 그러면 CPU는 SF플래그를 통해 a가 b보다 작았음을 알 수 있는 것이다.
레지스터의 호환
- 이 x86-64는 결국 IA-32의 64bit 버전으로 확장된 아키텍처다.
- 고로, 둘이 호환은 가능하다.
*어셈블리 언어*
- x64 어셈블리 언어
- 기본 구조.
- 기본적으로 **명령어(opcode) , 피연산자(operand)**로 이뤄진다.
- ex) mov rax, 3
- mov = 대입해라.
- rax = rax 에
- 3 = 3을 (진짜 말 그대로 3을)
- ex) mov rax, 3
- 기본적으로 **명령어(opcode) , 피연산자(operand)**로 이뤄진다.
- 데이터 이동(Data Transfer)
- mov : 데이터를 이동할때 사용함. (그 주소에 들어있는 ‘데이터’ 자체를)
- lea : 주소 값 자체를 옮길 때 사용한다.
- ex) lea rdx, [0x00ff213a] = 주소를 옮겨라. rdx의 주소를, 0x00ff213a로 (안에 있는 데이터 개무시하고 지금 당장 rdx가 지정하고 있는 주소를 바꾸는. 그런거?)
- 산술 연산(Arithmetic)
- inc : 말 그대로 값을 1 증가 시키는 명령어
- dec : 그 반대겠지? 값을 1 감소 시키는거임.
- add : 레지스터나 메모리의 값을 덧셈할 때 쓰는 명령어.
- sub : 그냥 add 반대. 저거에서 뺄셈 하는거임 ㅇㅇ
- 논리 연산 (= 디논 들었으면 다 알잖아)
- and
- or
- xor
- not
- 비교 (Comparison)
- cmp ( 두 인자를 ‘차’를 이용해서 비교하는 거임)
- test ( 두 인자를 ‘and’연산자를 이용해서 비교 하는거임)
- 분기(Branch)
- jmp (쓰임 - jmp addr) : addr로 rip를 이동시킨다.
- je (쓰임 - je addr) : 직전에 비교한 두 피연산자가 같으면 점프함. (jump if equal)
- jg (쓰임 - jg addr) : 직전에 비교한 두 연산자중 전자가 더 크면@!!!!! 점프함.
- 스택(stack) (SFP쪽에 붙어서 리턴값 붙혀주는?)
- push
- pop
- 프로시져(Procedure)
- call
- ret
- leave
- 시스템 콜 (Syscall)
- = syscall
- 기본 구조.
명령어(피연산자 기준)
종류?
- 상수 (말 그대로 상수)
- 레지스터(Register)
- 메모리 (memory)
- 여기서 ‘메모리’ 피연산자는 대괄호( [ ] )로 둘러싸인 것으로 표현되며, 앞에 크기 지정자인 Type PTR이 추가될 수 있다고 한다.
- 타입?
- BYTE - 1byte
- WORD - 2byte
- DWORD - 4byte
- QWORD - 8byte
- QWORD PTR [0x8048000]
- 0x8048000의 데이터를 8byte(QWORD)만큼 참조.
- DWORD PTR [0x8048000]
- 0x8048000의 데이터를 4byte(DWORD)만틈 참조.
- WORD PTR [rax]
- rax가 가르키는 주소에서 데이터를 2byte(WORD) 만큼 참조.
- cmp?
- cmp는 cmp op1, op2 로 쓰임.
- op1과 op2를 비교하는거임.
- cmp자체는 두 피연산자를 빼서 대소를 비교함.
- 그러나, 연산의 결과는 op1에 대입하지 않는다.
내가 헷갈린거, 16진수 나타내기, and 연산
자. 정리하자. 여기서 나는 어리석게도 0x10를 그냥 말 그대로 10이라고 생각함.
근데 저게 10진수로 16이라고 함.
왜 그러한가?
일단 0부터 15까지는 받아들일 수 있잖아 16진수가
0x00 0x01 … 0x0c 0x0d 0x0e 0x0f까지 0x0f는 15잖아.
그 다음에 16을 나타낼라면 결국 0001 0000이 되야하니까
즉, 0x10이 되야함.
고로 0f까지 가고 나서 자리올림이 일어나는 구조다. 0x는 ㅇㅋ?
register에서의 and연산.
rax : 0xffffffff00000000
rbx : 0x00000000ffffffff
rcx : 0x1234567abcdef0
- and rax, rcx
- and rbx, rcx
- or rax, rbx
코드 1 상황
- 0xffffffff00000000 and 0x123456789abcdef0 을 해야함.
그러면 이렇게 되는거임.
0x 1111 1111 1111 1111 1111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000
0x 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 0000
즉, 각 자리마다 and 연산을 해야함.
그러면 다음과 같은 결과를 도출해낼 수 있음.
0x 0001 0010 0011 0100 0101 0110 0111 1000 0000 0000 0000 0000 0000 0000 0000 0000
= 0x1234567800000000
코드 2 상황
- rbx와 rcx의 메모리? 값? 을 and 연산 해야함ㅇㅇ.
- 그러면 코드 1상황과 똑같이 행동하지만 대상은 rbx, rcx가 되는 것.
코드 3 상황
- 이건 or 상황이다.
'dreamhack > System hacking' 카테고리의 다른 글
Stage 1! (0) | 2023.08.11 |
---|