KOROMOON

착한 사마리아인이 되고 싶습니다.

9/09/2020

Basic Program Execution Registers


작성자 : KOROMOON

작성일 : 2013-12-09




( 1 ) Register


< 그림 – 컴퓨터 하드웨어 구성 >


레지스터(Register)는 컴퓨터의 프로세서 내에서 자료를 보관하는 아주 빠른 기억 장소임. 일반적으로 현재 계산을 수행 중인 값을 저장하는 데 사용됨. 대부분의 현대 프로세서는 메인 메모리에서 레지스터로 데이터를 옮겨와 데이터를 처리한 후 그 내용을 다시 레지스터에서 메인 메모리로 저장하는 로드-스토어 설계를 사용하고 있음.

레지스터는 메모리 계층의 최상위에 위치하며 가장 빠른 속도로 접근 가능한 메모리임.

우리가 일반적으로 메모리라고 얘기하는 RAM(Random Access Memory) 과는 조금 성격이 다름. CPU 가 RAM 에 있는 데이터를 액세스(Access)하기 위해서는 물리적으로 먼 길을 돌아가야 하기 때문에 시간이 오래 걸림. 하지만 레지스터는 CPU 와 한 몸이기 때문에 고속으로 데이터를 처리할 수 있음. 




( 2 ) IA-32


IA-32(또는 x86-32) 는 인텔의 32비트 마이크로프로세서에서 사용하는 명령 집합 아키텍처를 말함. (여기서 IA 는 Intel Architecture 의 약자임)

현재 많은 시장 점유율을 차지하고 있음. (고로 IA-32 Register 를 공부해야 함)

인텔은 IA-32 프로세서의 개발사이며 최대의 공급자임.




( 3 ) 레지스터에 대해서 알아야 하는 이유


리버싱 초급 단계에서 어플리케이션 디버깅을 잘 하려면 디버거가 해석(디스어셈)해주는 어셈블리 명령어를 공부해야 함. IA-32 에서 제공하는 어셈블리 명령어는 매우 방대한 양이라서 한번에 공부하기는 쉽지 않음. 디버깅을 해나가면서 모르는 명령어가 나올 때마다 Intel 에서 제공한 매뉴얼의 해당 명령어 설명을 살펴보면서 그때그때 공부하는 걸 추천함.


Intel® 64 and IA-32 Architectures Software Developer Manuals 다운로드 사이트 : 

https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html




( 4 ) IA-32 Register 종류


< 그림 - IA-32 Basic Execution Environment for Non-64-bit Modes >


IA-32 에서 지원하는 기능도 무척 많고 그 만큼 레지스터의 수도 많음. 어플리케이션 디버깅의 초급 단계에서는 Basic Program Execution Registers 에 대해서 알아야 함. 디버깅할 때 가장 많이 보게 될 레지스터임. 해당 문서에서는 Basic Program Execution Registers 에 대해서만 소개함.




( 5 ) Basic Program Execution Registers


< 그림 – Basic Program Execution Registers >


기본적인 프로그램 실행 레지스터로 IA-32 아키텍처는 일반적으로 시스템 및 응용 프로그램에서 사용하기 위해 16개 기본 프로그램 실행 레지스터를 제공함.


분류

설명

General-Purpose Registers

(범용 레지스터)

피연산자와 포인터를 저장하여 사용하는 레지스터

(32 비트, 8)

Segment Registers

(세그먼트 레지스터)

세그먼트 셀렉터

(16 비트, 6)

EFLAGS Register

프로그램를 실행하거나 프로세스 제한을 제어하는 레지스터

(32 비트, 1)

EIP Register

다음 명령어 실행을 저장하는 포인터 레지스터

(32 비트, 1)




( 6 ) General-Purpose Registers [범용 레지스터]


< 그림 – General-Purpose Registers >


범용 레지스터는 범용적으로 사용하는 레지스터로 보통 상수/주소 등을 저장할 때 주로 사용되며 특정 어셈블리 명령어에서는 특정 레지스터를 조작하기도 함.

또한 어떤 레지스터들은 특수한 용도로 사용되기도 함.

IA-32 에서는 각각의 범용 레지스터들의 크기는 32 비트(4 바이트)임.


분류

설명

EAX

Extended Accumulator Register 의 약자임.

산술, 논리 연산을 수행하며 함수의 리턴 값에 사용됨.

, 덧셈, 곱셈, 나눗셈 등의 명령은 모두 EAX 레지스터를 사용하며 함수의 리턴 값이 EAX 레지스터에 저장되므로 호출 함수의 성공 여부, 실패 여부를 쉽게 파악할 수 있으며 리턴 값을 쉽게 얻을 수 있음.

모든 Win32 API 함수들은 리턴 값을 EAX 에 저장한 후 리턴함.

EBX

Extended Base Register 의 약자임.

ESI 레지스터나 EDI 레지스터와 결합될 수 있으며 메모리 주소를 저장하기 위한 용도로 사용됨.

ECX

Extended Counter Register 의 약자임.

반복문 명령어(LOOP)에서 반복 카운터(Loop Counter)로 사용됨.

ECX 레지스터에 반복할 횟수를 지정하고 반복 작업을 수행함.

EDX

Extended Data Register 의 약자임.

EAX 와 같이 쓰이고 부호 확장 명령 등에 쓰임.

큰 수의 곱셈 또는 나눗셈 등의 연산이 이루어질 때 EDX 레지스터가 사용되어 EAX 레지스터와 함께 쓰임.

ESI

Extended Source Index 의 약자임.

데이터를 조작하거나 복사시에 소스 데이터의 주소가 저장됨.

ESI 레지스터가 가리키는 주소의 데이터를 EDI 레지스터가 가리키는 주소로 복사하는 용도로 많이 사용됨.

ESI EDI 는 특정 명령어들(LODS, STOS, REP MOVS )과 함께 주로 메모리 복사에 사용됨.

EDI

Extended Destination Index 의 약자임.

데이터를 조작하거나 복사시에 목적지의 주소가 저장됨.

주로 ESI 레지스터가 가리키는 주소의 데이터가 복사될 곳의 주소임.

ESI EDI 는 특정 명령어들(LODS, STOS, REP MOVS )과 함께 주로 메모리 복사에 사용됨.

EBP

Extended Base Pointer 의 약자임.

하나의 스택 프레임의 시작 지점 주소를 저장함.

현재 사용되는 스택 프레임이 소멸되지 않는 동안 EBP 의 값은 불변함.

현재의 스택 프레임이 소멸되면 이전에 사용되던 스택 프레임을 가리키게 됨. (스택 프레임 기법을 꼭 알아두자!)

ESP

Extended Stack Pointer 의 약자임.

하나의 스택 프레임의 끝 지정 주소를 저장함.

PUSH, POP 명령어에 따라서 ESP 의 값이 4 바이트씩 변함


참고로 Windows 어셈블리 프로그래밍에서 주의할 점!

Win32 API 함수들은 내부에서 ECX 와 EDX 를 사용함. 따라서 이런 API 가 호출되면 ECX 와 EDX 의 값은 변경되어 버림. ECX 와 EDX 에 중요한 값이 저장되어 있다면 API 함수 호출 전에 다른 레지스터나 스택에 백업해야 함.




( 7 ) Segment Registers [세그먼트 레지스터]


< 그림 – 세그먼트 메모리 모델 >


세그먼트(Segment) 기법은 IA-32 보호 모드에서 메모리를 조각내어 각 조각마다 시작 주소, 범위, 접근 권한 등을 부여해서 메모리를 보호하는 기법을 말함. 또한 페이징(Paging) 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할 때 사용됨. 세그먼트 메모리는 SDT(Segment Descriptor Table) 이라고 하는 곳에 기술되어 있는데 세그먼트 레지스터는 바로 이 SDT 의 Index 를 가짐.

세그먼트 레지스터는 총 6개(CS, SS, DS, ES, FS, GS)이며 각각의 크기는 16 비트(2 바이트)임.

각 세그먼트 레지스터가 가리키는 세그먼트 디스크립터(Segment Descriptor)와 가상 메모리가 조합되어 선형 주소(Linear Address)가 페이징 기법에 의해서 선형 주소가 최종적으로 물리 주소(Physical Address)로 변환됨.


분류

설명

CS

Code Segment 약자로 코드 세그먼트임.

프로그램의 코드 세그먼트의 시작 주소를 포함함. 이 세그먼트 주소에 명령어 포인터(Instruction Pointer, IP) 레지스터의 오프셋 값을 더하면 실행하기 위해 메모리로부터 가져와야 할 명령어의 주소가 됨.(명령어 코드의 베이스 주소 저장) 일반적인 프로그래밍에서는 이 레지스터를 직접 참조할 필요가 없음.

SS

Stack Segment 약자로 스택 세그먼트임.

메모리 상에 스택의 구현을 가능하게 함. 프로그램은 주소와 데이터의 임시 저장 목적으로 스택을 사용함. 시스템은 프로그램의 스택 세그먼트의 시작 주소를 SS 레지스터에 저장함. 이 세그먼트 주소에 스택 포인터(Stack Pointer, SP) 레지스터의 오프셋 값을 더하면 참조되고 있는 스택의 현재 워드를 가리킴.(스택의 베이스 주소 저장) 일반적인 프로그램에서는 이 레지스터를 직접 참조할 필요가 없음.

DS

Data Segment 약자로 데이터 세그먼트임.

프로그램의 데이터 세그먼트의 시작 주소를 포함함. 명령어는 이 주소를 사용하여 데이터의 위치를 알아냄. 이 주소에 명령어의 오프셋 값을 더하면 데이터 세그먼트에 속한 특정 바이트 위치에 대한 참조가 생성됨. 참고로 SI(Source Index) 레지스터와 연관됨.

ES

Extra(Data) Segment 약자로 추가적인 데이터 세그먼트임.

메모리 주소 지정을 다루는 스트림(문자 데이터) 연산에서 사용됨. 이 경우 ES 레지스터는 DI(Destination Index) 레지스터와 연관됨. 프로그램이 ES 레지스터를 사용할 경우에 이 레지스터를 적절한 세그먼트 주소로 초기화해야 함. 참고로 DS 레지스터의 확장 세그먼트로 사용 가능함.

FS

추가적인 데이터 세그먼트임.

어플리케이션 디버깅에도 자주 등장하는데 SEH(Structured Exception Handling), TEB(Thread Environment Bloc), PEB(Process Environment Block) 등의 주소를 계산할 때 사용됨.

GS

추가적인 데이터 세그먼트임.




( 8 ) EFLAGS Register


< 그림 – EFLAG Register >


EFLAGS Register 는 16 비트의 FLAGS 레지스터의 32 비트 확장 형태로 목적에 따라 상태 플래그, 제어 플래그, 시스템 플래그로 나뉨. 전부 이해한다는 것은 상당히 어려운 일이며 리버싱 입문 단계에서는 어플리케이션 디버깅에 필요한 상태 플래그에 대해서만 잘 이해하면 됨.

상태 플래그가 중요한 이유는 특히 조건 분기 명령어에서 이들 플래그의 값을 확인하고 그에 따라 동작 수행 여부를 결정하기 때문임.


이름

비트 번호

설명

CF

(Carry Flag)

0

산술연산 수행 결과가 자리 올림이나 자리 내림이 발생할 때 set(1) .

상태 플래그 중 유일하게 어셈블리어 STC, CLC, CMC Instruction을 이용하여 플래스 값을 직접 수정 가능한 플래그임.

OF

(Overflow Flag)

11

산술연산 결과의 오버플로우 상태를 표시.

(부호있는 수의 연산에서 표현 가능한 수의 범위를 초과 했음을 나타냄.)

연산의 결과값이 최상위 비트(MSB)을 제외한 최대 허용 정수값보다 크거나 최소 정수보다 작으면 set(1) .

SF

(Sign Flag)

7

최상위 비트(MSB)의 결과와 같은 결과로 set .

Singned Interger로 사용되는 경우 이 플래그가 0이면 양수, 1이면 음수 나타냄.

예를 들면 간단하게 8비트로 가정하면 부호있는 정수가 0000 0001이면 MSB 0이므로 SF 0(양수)이 되기 때문에 1이지만, 1000 0001일 경우 MSB 1이므로 SF 1(음수)가 되어 2의 보수값을 취해보면 0111 1111이 되므로 -255임을 알 수 있음.

ZF

(Zero Flag)

6

연산 결과가 0으로 되었을 때 set(1) .

비교연산 ‘=’ 때도 set(1) .

값을 비교할 때 내부적으로 두 값을 서로 빼서 값은 값이면 0이 되므로 값은 값을 구하는 수식, 루프에서 카운트값을 점차적으로 빼서 0이 되면 루프를 빠져 나오게 하는 등 여러 방식으로 응용되는 플래그임.


CF 와 OF는 산술 연산 후 오버플로우가 발생했는지에 대한 상태 정보를 알려주는 플래그임.

두 플래그의 차이점은 Signed 연산에 대해 오버플로우가 발생했을 때는 OF가 1로 설정되고, Unsigned 연산에 대해 오버플로우가 발생했을 때는 CF가 1로 설정됨.


여기서 Signed 연산과 Unsigned 연산에 대한 이해가 필요함.

Signed 연산이란 연산에 사용되는 오퍼랜드를 Signed 데이터 타입으로 간주하고 연산을 한다는 것이며, Unsigned 연산은 연산에 사용되는 오퍼랜드를 Unsigned 데이터 타입으로 간주하고 연산을 한다는 것임.

Signed 데이터 타입이란 양수 혹은 음수를 표현할 수 있는 데이터 타입이며, 이를 위해 최상위 비트가 부호 비트로 사용되는 데이터 타입임.

Unsigned 데이터 타입은 음수를 표현할 수 없고, Signed 데이터 타입에서 부호 비트로 사용되었던 최상위 비트는 수를 표현하기 위한 일반 비트로 사용됨.

덧셈과 뺄셈의 경우에 Signed/Unsigned 연산의 바이너리 결과값이 동일하기 때문에 IA-32에서는 각각 ADD와 SUB명령어 한 개만을 제공하고 있으며, 곱셈과 나눗셈의 경우 Signed/Unsigned 연산의 바이너리 결과값이 다르기 때문에 Signed 버전 명령어와 Unsigned 버전 명령어를 따로 제공하고 있음.


마지막으로 연산이 Signed/Unsigned 에 따라 오버플로우 여부를 판단하는 방법은 다르다.

Signed 연산의 경우 오버플로우 여부는 OF를 확인해야 하고, Unsigned 연산의 경우 오버플로우 여부는 CF를 확인해야 함.




( 9 ) EIP Register


EIP Register 는 16 비트의 IP(Instruction Pointer) 레지스터의 32 비트 확장 형태임.

CPU 는 EIP 에 저장된 메모리 주소의 명령어를 하나 처리하고 난 후 자동으로 그 명령어 길이만틈 EIP 를 증가시킴. 이런 식으로 계속 명령어를 처리해 나감.

범용 레지스터들과는 다르게 EIP 는 그 값을 직접 변경할 수 없도록 되어 있어서 다른 명령어를 통하여 간접적으로 변경해야 함. EIP 를 변경하고 싶을 때는 특정 명령어(JMP, Jcc, CALL, RET)를 사용하거나 인터럽트(Interrupt), 예외(Exception)를 발생시켜야 함.




============================================================
본 게시물은 KOROMOON 님께서 작성하였으며 CCL (Creative Commons License) 에서 "저작자표시-비영리-동일조건변경허락" 이용조건으로 자료를 이용하셔야 합니다.

댓글 없음:

댓글 쓰기