[Reversing] 스택

 

리버스 엔지니어링


(1) 스택

 

1-1. 스택이란?

  • 함수 내의 로컬 변수 임시 저장
  • 함수 호출 시 파라미터 전달
  • 복귀 주소(return address) 저장

위와 같은 역할을 수행하기에는 스택의 LIFO(Last In First Out) 구조가 아주 유용하다.

 

 

1-1-1. 스택 특징

 

[그림 1] 스택

 

[그림 1]의 내용을 간단히 설명하자면, 다음과 같다.

 

  • 프로세스에서 스택 포인터(ESP)의 초기 값은 Stack Bottom쪽(그림 아래쪽)에 가까움
  • PUSH 명령에 의해서 Stack에 값이 추가되면 스택 포인터는 Stack Top을 향해 (위쪽으로) 움직임
  • POP 명령에 의해 스택에서 값이 제거되면 스택 포인터는 Stack Bottom을 향해 (아래쪽으로) 움직임
  • 즉 높은 주소에서 낮은 주소 방향으로 스택이 자라남

위 그림으로 보면 아래에서 윗 방향으로 스택이 자라난다.

 

이러한 스택의 특성 때문에 보통 "스택은 거꾸로 자란다."라는 표현을 쓰기도 한다.

 

이렇게 하는 이유는 스택이라는 단어는 뭔가를 쌓는다는 뜻이기 때문에 쌓을수록 올라오는 것이 직관적이기 때문이다.

 

 

1-1-2. 스택 특징 및 동작

  • 스택에 값을 입력하면 스택 포인터(ESP)는 감소하고, 스택에서 값을 꺼내면 스택 포인터는 증가
  • 스택 포인터의 초기 값은 스택 메모리의 아래 쪽에 위치
  • PUSH 명령일 경우, 스택에 값을 집어 넣어 ESP가 위쪽 방향으로 이동 (4바이트만큼 줄어듦)
  • POP 명령일 경우, 스택에서 값을 꺼내 ESP는 아래 방향으로 이동 (4바이트만큼 증가)

 

[그림 2] 스택 동작 원리

 

# 스택 추가 설명

  • 제한적으로 접근할 수 있는 나열 구조
  • 접근 방법은 언제나 목록 끝에서만 일어나며, 끝 먼저내기 목록이라고도 함
  • 한 쪽 끝에서만 자료를 넣거나 뺄 수 있는 선형 구조(LIFO)로 되어 있음
  • 자료를 넣는 것을 '밀어넣는다' 하여 PUSH라 함
  • 반대로 넣어둔 자료를 '꺼낸다' 하여 POP이라 함
  • 이때, 꺼내지는 자료는 가장 최근에 PUSH한 자료부터 나오게 되며, 나중에 넣은 값이 처음에 나오는 것을 LIFO라 함

# Reference

 

http://www.yes24.com/Product/Goods/7529742

 

https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%83%9D

'Reversing' 카테고리의 다른 글

[Reversing] 함수 호출 규약  (0) 2020.03.26
[Reversing] Process Explorer  (0) 2020.03.26
[Reversing] IA-32 Register  (0) 2020.03.25
[Reversing] 리틀 엔디언 표기법  (0) 2020.03.24
[Reversing] OllyDbg 사용법  (0) 2020.03.24

[Reversing] IA-32 Register

 

리버스 엔지니어링


(1) IA-32(Intel Architecture 32bit) Register

1-1. CPU 레지스터란?

  • CPU 내부에 존재하는 다목적 저장 공간

  • 일반적으로 메모리라고 얘기하는 RAM(Random Access Memory)와는 성격이 다름

  • CPU가 RAM에 있는 데이터를 액세스(Access)하기 위해서는 물리적으로 먼 길을 돌아가야 하기 때문에 오랜 시간이 걸림

  • 하지만 레지스터는 CPU와 한 몸이기 때문에 고속으로 데이터 처리 가능

어셈블리 명령어의 대부분은 레지스터를 조작하고 그 내용을 검사하는 것들이므로, 레지스터를 확실히 이해해야 명령어 자체도 이해가 가능하다.


1-2. IA-32 레지스터 종류

 

IA-32는 지원하는 기능도 무척 많고, 그만큼 레지스터의 수도 많다.

 

IA-32에 존재하는 레지스터들의 종류는 다음과 같다.

 

  • Basic program execution registers

    • x87 FPU registers

    • MMX registers

    • XMM registers

    • Control registers

    • Memory management registers

    • Debug registers

    • Memory type range registers

    • Machine specific registers

    • Machine check register

애플리케이션 디버깅의 초급 단계에서는 Basic program execution register에 대해서 알아두어야 한다.

 

디버깅할 때 가장 많이 보게 될 레지스터이며, 중/고급 단계에서는 추가적으로 Control registers, Memory management registers, Debug registers 등에 대해서도 알아두어야 한다.

 

 

1-2-1. Basic program execution registers

 

Basic program execution registers는 다시 4개의 그룹으로 나눌 수 있다.

 

  • General Purpose Registers (32비트 - 8개)

  • Segment Registers (16비트 - 6개)

  • Program Status and Control Register (32비트 - 1개)

  • Instruction Pointer (32비트 - 1개)

 

[그림 1] Basic program execution registers

 

# 참고

  • 레지스터 이름에 E(Extend)가 붙은 경우는 예전 16비트 CPU인 IA-16 시절부터 존재하던 16비트 크기의 레지스터들을 32비트 크기로 확장시켰다는 뜻

 

각 그룹에 대하여 살펴보도록 하자.

 

1) 범용 레지스터 (General Purpose Registers)

  • 이름처럼 범용적으로 사용되는 레지스터들

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

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

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

 

[그림 2] 범용 레지스터

 

각 레지스터들은 16비트 하위 호환을 위하여 몇 개의 구획으로 나뉘어진다. (EAX 기준)

 

  • EAX : (0 ~ 31) 32비트

  • AX : (0 ~ 15) EAX의 하위 16비트

  • AH : (8 ~ 15) AX의 상위 8비트

  • AL : (0 ~ 7) AX의 하위 8비트

즉 4바이트(32비트)를 다 사용하고 싶을 때는 EAX를 사용하고, 2바이트(16비트)만 사용할 때는 EAX의 하위 16비트 부분인 AX를 사용하면 된다.

 

AX는 다시 상위 1바이트(8비트)인 AH와 하위 1바이트(8비트)인 AL로 나뉘어진다.

 

이런 식으로 하나의 32비트 레지스터를 상황에 맞게 8비트, 16비트, 32비트로 알뜰하게 사용 가능하다.

 

 

각 레지스터의 이름은 아래와 같다.

 

  • EAX : Accumulator for operands and results data

  • EBX : Pointer to data in the DS segment

  • ECX : Counter for string and loop operations

  • EDX : I/O pointer

위 4개의 레지스터들은 주로 산술 연산(ADD, SUB, XOR, OR 등) 명령어에서 상수/변수 값의 저장 용도로 많이 사용된다.

 

어떤 어셈블리 명령어(MUL, DIV, LODS 등)들은 특정 레지스터를 직접 조작하기도 한다.

(명렁어 실행 뒤에 특정 레지스터들의 값이 변경) 

 

그리고 추가적으로 ECX와 EAX는 특수 용도로 사용되며, ECX는 반복문 명령어(LOOP)에서 반복 카운트(loop count)로 사용된다. (루프를 돌 때마다 ECX를 1씩 감소시킴)

 

EAX는 일반적으로 함수 리턴 값에 사용되며, 모든 Win32 API 함수들은 리턴 값을 EAX에 저장한 후 리턴한다.

 

 

# 참고

  • Win32 API 함수들은 내부에서 ECX와 EDX를 사용

  • 따라서 이런 API가 호출되면 ECX와 EDX의 값은 변경됨

  • 따라서 ECX와 EDX에 중요한 값이 저장되어 있다면 API 호출 전에 다른 레지스터나 스택에 백업 필요

 

나머지 범용 레지스터들의 이름은 아래와 같다.

 

  • EBP : Pointer to data on the stack (in the SS segment)

  • ESI : source pointer for string operations

  • EDI : destination pointer for string operations

  • ESP : Stack pointer (in the SS segment) 

위 4개의 레지스터들은 주로 메모리 주소를 저장하는 포인터로 사용된다.

 

ESP는 스택 메모리 주소를 가리키며, 어떤 명령어들(PUSH, POP, CALL, RET)은 ESP를 직접 조작하기도 한다.

(스택 메모리 관리는 프로그램에서 매우 중요하기 때문에 ESP를 다른 용도로 사용하지 말아야 한다.)

 

EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가, 함수가 리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 깨지지 않도록 하며, 이것을 Stack Frame 기법이라 한다.

 

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

 

 

2) 세그먼트 레지스터

  • 세그먼트(Segment)란 IA-32의 메모리 관리 모델에서 나오는 용어

  • IA-32 보호 모드에서 세그먼트란 메모리를 조각내어 각 조각마다 시작 주소, 범위, 접근 권한 등을 부여해서 메모리를 보호하는 기법을 말함

  • 세그먼트는 페이징(Paging) 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할 때 사용

  • 세그먼트 메모리는 Segnet Descriptor Table(SDT)이라고 하는 곳에 기술되어 있는데, 세그먼트 레지스터는 바로 SDT의 index를 가짐

 

[그림 3] 세그먼트 메모리 모델

 

[그림 3]은 보호 모드에서의 세그먼트 메모리 모델을 나타낸다.

 

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

 

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

 

만약 OS에서 페이징을 사용하지 않는다면 선형 주소는 그대로 물리 주소가 된다.

 

 

각 세그먼트 레지스터 이름은 다음과 같다.

  • CS : Code Segment

  • SS : Stack Segment

  • DS : Data Segment

  • ES : Extra(Data) Segment

  • FS : Data Segment

  • GS : Data Segment

이름 그대로 CS는 프로그램의 코드 세그먼트를 나타내며, SS는 스택 세그먼트, DS는 데이터 세그먼트를 나타낸다.

 

EF, FS, GS 세그먼트는 추가적인 데이터 세그먼트이다.

 

FS 레지스터는 애플리케이션 디버깅에도 자주 등장하는데 SEH(Structured Ex-ception Handling), TEB(Thread Environment Block), PEB(Process Environment Block) 등의 주소를 계산할 때 사용된다.

 

 

 

3) 프로그램 상태와 컨트롤 레지스터

  • EFLAGS : Flag Register

  • 플래그(Flag) 레지스터 이름은 EFLAGS이며, 32비트(4바이트) 크기

  • EFLAGS 레지스터 역시 16비트의 FLAGS 레지스터의 32비트 확장 형태

[그림 4] EFLAGS Register

 

EFLAGS 레지스터는 [그림 4]와 같이 각각의 비트마다 의미를 가지고 있으며, 각 비트는 1 또는 0의 값을 가진다.

 

이는 On/Off 혹은 True/False를 의미한다.

 

일부 비트는 시스템에서 직접 세팅하고, 일부 비트는 프로그램에서 사용된 명령의 수행 결과에 따라 세팅된다.

 

 

# 참고

  • Flag는 단어 그대로 깃발이 올라가면 1(On/True), 내려가면 0(Off/False)

  • flag(ZF, OF, CF)가 중요

  • 이유는 조건 분기 명령어(Jcc)에서 이들 Flag의 값을 확인하고 그에 따라 동작 수행 여부를 결정하기 때문

 

3개의 플래그 내용은 다음과 같다.

 

  • Zero Flag(ZF) : 연산 명령 후에 결과 값이 0이 되면 ZF가 1(True)로 세팅

  • Overflow Flag(OF) : 부호 있는 수(signed integer)의 오버플로가 발생했을 때 1로 세팅되며, MSB(Most Significant Bit)가 변경되었을 때 1로 세팅

  • Carry Flag(CF) : 부호 없는 수(unsigned integer)의 오버플로가 발생했을 때 1로 세팅

 

 

4) Instruction Pointer

  • EIP : Instruction Pointer

  • CPU가 처리할 명령어의 주소를 나타내는 레지스터

  • 크기는 32비트(4바이트)이며, 16비트의 IP 레지스터의 확장 형태

  • CPU는 EIP에 저장된 메모리 주소의 명령어(instruction)를 하나 처리하고 난 후 자동으로 그 명령어 길이만큼 EIP를 증가시킴

  • 이런 형식으로 계속 명령어를 처리해 나감

범용 레지스터들과 다르게 EIP는 그 값을 직접 변경할 수 없도록 되어 있어서 다른 명령어를 통하여 간접적으로 변경해야 한다.

 

EIP를 변경하고 싶을 때는 특정 명령어(JMP, Jcc, CALL, RET)를 사용하거나 인터럽트(Interrupt), 예외(Exception)를 발생시켜야 한다.


# Reference

 

http://www.yes24.com/Product/Goods/7529742

'Reversing' 카테고리의 다른 글

[Reversing] Process Explorer  (0) 2020.03.26
[Reversing] 스택  (0) 2020.03.25
[Reversing] 리틀 엔디언 표기법  (0) 2020.03.24
[Reversing] OllyDbg 사용법  (0) 2020.03.24
[Reversing] 리버스 엔지니어링이란?  (0) 2020.01.04

[Reversing] 리틀 엔디언 표기법

 

리버스 엔지니어링


(1) 바이트 오더링

  • 바이트 오더링은 데이터를 저장하는 방식을 뜻함

  • 이는 애플리케이션의 디버깅할 때, 알아두어야 하는 기본 개념 중 하나

  • 빅 엔디언(Big Endian)과 리틀 엔디언(Little Endian) 두 가지 방식이 존재

 

예제 코드를 보면서 이해해보도록 하자.

 

[그림 1] 예제 코드

 

총 4개의 크기가 다른 자료형이 있다.

 

각 엔디언 방식에 따라서 같은 데이터를 어떤 형식으로 저장하는지 비교해보도록 하자.

 

 

[표 1] 빅 엔디언과 리틀 엔디언 차이점

Type Name Size 빅 엔디언 리틀 엔디언
BYTE b 1 [12] [12]
WORD w 2 [12][34] [34][12]
DWORD dw 4 [12][34][56][78] [78][56][34][12]
char [] str 6 [61][62][63][64][65][00] [61][62][63][64][65][00]

 

#참고

  • ASCII 문자 'a'는 0x61과 같고, 'e'는 0x65와 같음

  • 그리고 문자열 마지막은 NULL로 끝이 남

 

바이트(BYTE) 타입의 b 변수를 저장할 때는 두 방식의 차이가 없지만, 2바이트 이상 크기를 가진 자료형을 저장할 때부터 차이가 나타난다.

 

빅 엔디언 방식은 데이터를 저장할 때 사람이 보는 방식과 동일하게 앞에서부터 순차적으로 저장한다.

 

하지만, 리틀 엔디언 방식은 데이터를 저장할 때 역순으로 저장한다.

 

즉, 저장되는 바이트의 순서가 뒤집어져 있다.

 

리틀 엔디언이라고 할지라도 바이트 자체는 정상적인 순서로 저장이 된다.

 

오로지 2바이트 혹은 4바이트 자료형과 같이 멀티바이트(multi-bytes)인 경우 각 바이트가 역순으로 저장되는 것이다.

 

또한 str 문자열은 Endian 형식에 상관없이 동일하다.

 

이유는 문자열이란 결국 캐릭터(char) 배열이기 때문에 각 바이트를 하나씩 연속해서 저장한다고 생각해보면 리틀 엔디언에서도 문자열 자체는 빅 엔디언과 동일한 순서로 저장되는 것이다.


1-1. 리틀 엔디언 & 빅 엔디언

  • 데이터를 순서대로 저장시키는 빅 엔디언 장점은 사람이 보기에 직관적이라는 것

  • 빅 엔디언은 대형 UNIX 서버에 사용되는 RISC 계열 CPU에서 많이 사용되며, 네트워크 프로토콜에도 사용

  • 이것은 x86 계열의 응용 프로그램 개발자와 리버서에게 중요한 의미를 가짐

  • 애플리케이션 개발에 사용된 데이터를 네트워크로 송수신할 때 엔디언 타입을 변경해야 하기 때문

바이트 오더링이 빅 엔디언으로만 사용된다면 굳이 설명이 필요 없을 것이다.

 

문제는 Intel x86 CPU에서 리틀 엔디언 방식을 사용한다는 것이다.

 

따라서 Windows 계열 리버서들은 리틀 엔디언에 대해서 잘 알고 있어야 한다.

 

데이터를 역순으로 저장시키는 리틀 엔디언 방식도 산술 연산과 데이터의 타입이 확장/축소 될 때 더 효율적이라는 장점을 가진다.


1-2. OllyDbg에서 리틀 엔디언 확인

 

[그림 2] LittleEndian.cpp

 

코드를 빌드하여 LittleEndian.exe를 생성한 후 OllyDbg로 디버깅을 진행한다.

 

[그림 3] main() 함수

 

[그림 3]을 보면 main() 함수의 주소는 401000이다.

 

그리고 전역 변수들의 주소는 40AC40(b), 40AC48(w), 40AC48(dw), 40AC4C(str)이다.

 

이 메모리 영역을 OllyDbg의 데이터 창에서 살펴보겠다.

(Goto 명령[Ctrl+G]을 통해 40AC40으로 이동)

 

 

[그림 4] 전역 변수 메모리 영역

 

변수 w와 dw 값들이 리틀 엔디언 형식으로 저장된 것을 확인할 수 있다.


# Reference

 

http://www.yes24.com/Product/Goods/7529742

'Reversing' 카테고리의 다른 글

[Reversing] Process Explorer  (0) 2020.03.26
[Reversing] 스택  (0) 2020.03.25
[Reversing] IA-32 Register  (0) 2020.03.25
[Reversing] OllyDbg 사용법  (0) 2020.03.24
[Reversing] 리버스 엔지니어링이란?  (0) 2020.01.04

[Reversing] OllyDbg 사용법

 

리버스 엔지니어링


(1) Hello World! 프로그램

 

아마도 모든 개발자가 처음 만들어보는 프로그램은 'Hello World!'일 것이다.

 

Hello World 프로젝트르 다음과 같이 열어 실행 시켜보도록 하자.

 

[그림 1] HelloWorld.cpp

 

[그림 2] HelloWorld.exe 실행

 

 

1-1. 디버거와 어셈블리 언어

  • 개발 도구(Visual C++)를 이용해 소스 코드(HelloWorld.cpp)를 빌드하면, 실행 파일(HelloWorld.exe)이 생성

  • 이 과정은 결국 사람이 이해하기 쉬운 소스 코드(HelloWorld.cpp)를 기계가 이해하기 쉬운 기계어(HelloWorld.exe)로 변환하는 것

  • 이러한 기계어는 사람이 알아보기 어렵기 때문에 좀 더 편하게 보기 위해 디버거(Debugger) 유틸리티를 사용

  • 디버거에 탑재된 디스어셈블러(Disassembler) 모듈은 이 기계어를 어셈블리(Assembly) 언어로 번역해서 보여줌

 

# 참고 내용

  • 실행 파일을 생성하는 어떠한 프로그래밍 언어라도 빌드 과정을 거치면 모두 기계어로 변환 됨

  • 디버거를 통해 어떤 실행 파일이라도 어셈블리 언어로 번역해서 볼 수 있기 때문에 리버서는 기본적으로 어셈블리 언어 지식 필요

  • 어셈블리 언어만 잘 익혀 놓으면 실행 파일이 어떠한 프로그래밍 언어로 제작되었는지 상관없이 디버깅을 통한 코드 분석 가능

  • 어셈블리 언어는 CPU에 종속되어 있음

  • 즉, 일반 PC에서 많이 사용되는 Intel x86 계열 CPU와 모바일 제품에서 많이 사용되는 ARM 계열 CPU는 서로 어셈블리 명령어 형태가 다음

 

 

1-2. OllyDbg 사용

 

[그림 3] OllyDbg 기본 화면

 

다운로드 사이트 : http://www.ollydbg.de/

 

OllyDbg v1.10

 

www.ollydbg.de

 

# 참고 내용

  • 일반적으로 파일을 분석할 때, 소스 코드가 없이 실행 파일만 가지고 분석을 하므로 OllyDbg 같은 Win32 전문 디버거 사용

  • OllyDbg는 직관적 인터페이스와 강력한 확장 기능으로 무장한 Win32 디버거

  • 무료로 제공되며, 가볍고 빠르기 대문에 많은 리버서들이 애용하는 디버거

  • 다른 도구로는 Hex-Rays 사의 IDA-Pro 사용 추천 (최강의 유틸리티로 수 많은 기능 지원)

 

[표 1] OllyDbg 메인 화면 구성

Code Window 기본적으로 disassembly code 표시하여 각종 comment, label을 보여주며, 코드를 분석하여 loop, jump 위치 등의 정보 표시
Register Window CPU register 값을 실시간으로 표시하며 특정 register들은 수정 가능
Dump Window 프로세스에서 원하는 memory 주소 위치를 Hex와 ASCII/유니코드 값으로 표시하고 수정 가능
Stack Window ESP register가 가리키는 프로세스 stack memory를 실시간 표시하고 수정 가능

 

다음 [그림 4]와 [표 2]는 Code Window에 대한 설명이다.

 

[그림 4] Code Window 

[표 2] Code Window 역할

Address 프로세스의 가상 메모리(Virtual Address : VA) 내의 주소
Instruction IA32(또는 x86) CPU 명령어
Disassembled code OP code를 보기 쉽게 어셈블리로 변환한 코드
comment 디버거에서 추가한 주석(옵션에 따라 약간 다르게 보임), 빨간색 글씨 부분은 API 함수 이름

 

 

EP(Entry Point)

  • Windows 실행 파일(EXE, DLL, SYS 등)의 코드 시작점을 의미

  • 프로그램이 실행될 때 CPU에 의해 가장 먼저 실행되는 코드 시작 위치

 

본격적인 디버깅을 하기 위해서 OllyDbg의 기본 명령어 사용법을 이해해야 한다.

 

 

[표 3] OllyDbg 기본 명령어(Code Window)

명령어 단축키 설명
Restart [Ctrl] + [F2] 다시 처음부터 디버깅 시작(디버깅을 당하는 프로세스를 종료 및 재실행)
Step Into [F7] 하나의 OP code 실행(CALL 명령을 만나면, 그 함수 코드 내부로 따라 들어감
Step Over [F8] 하나의 OP code 실행(CALL 명령을 만나면, 따라 들어가지 않고 함수 자체를 실행 
Execute till Return [Ctrl] + [F9] 함수 코드 내에서 RETN 명령어까지 실행(함수 탈출 목적)

(2) 디버거 명령어

 

2-1. 디버거 동작 명령

 

추가적인 디버깅 명령어를 사용하는 방법은 다음과 같다.

 

[표 4] 디버거 동작 명령(Code Window)

명령어 단축키 설명
Go to [Ctrl] + [G] 원하는 주소로 이동(코드/메모리 확인 시 사용, 실행은 아님)
Execute till Cursor [F4] cursor 위치까지 실행(디버깅하고 싶은 주소까지 바로 갈 수 있음)
Comment ; Comment 추가
User-defined comment 마우스 우측 메뉴 Search for - User-defined comment 사용자가 입력한 comment 목록 보기
Label : Label 추가
User-defined label 마우스 우측 메뉴 Search for- User-defined label 사용자가 입력한 Label 목록 보기
Set/Reset BreakPoint [F2] BP 설정 / 해제
Run [F9] 실행(BP가 걸려있으면 그곳에서 실행 정지)
Show the current EIP * 현재 EIP 위치를 보여줌
Show the previous Cursor - 직전 커서 위치를 다시 보여줌
Preview CALL/JMP address [Enter] 커서가 CALL / JMP 등의 명령어에 위치해 있다면, 해당 주소를 따라가서 보여줌
(실행되는 것이 아니며, 간단히 함수 내용 확인 시에 유용)
All referenced text strings 마우스 우측 메뉴 Search for - All referenced text strings 코드에서 참조되는 문자열 보기
All intermodular calls 마우스 우측 메뉴 Search for - All intermodular calls 코드에서 호출되는 모든 API 함수 보기
Name in all modules 마우스 우측 메뉴 Search for - Name in all modules 모든 API 함수 보기
Edit data Ctrl + [E] 데이터 편집
Assemble [Space] 어셈블리 코드 작성
Copy to executable file 마우스 메뉴 Copy to executable file 파일의 복사본 생성(변경 사항 반영)

 

다음은 Assembly에 대한 기초적인 명령어이다.

 

[표 5] Assembly 기초 명령어

명령어 설명
CALL XXXX XXXX 주소의 함수 호출
JMP XXXX XXXX 주소로 점프
PUSH XXXX 스택에 XXXX 저장
RETN 스택에 저장된 복귀 주소로 점프

 

다음은 리버싱과 관련된 중요한 용어들이다.

 

[표 6] 용어 정리

용어 설명
VA(Virtual Address) 프로세스 가상 메모리
OP code(OPeration code) CPU 명령어(바이트 code)
PE(Portable Executable) Windows 실행 파일(EXE, DLL, SYS 등)

# Reference

 

http://www.yes24.com/Product/Goods/7529742

'Reversing' 카테고리의 다른 글

[Reversing] Process Explorer  (0) 2020.03.26
[Reversing] 스택  (0) 2020.03.25
[Reversing] IA-32 Register  (0) 2020.03.25
[Reversing] 리틀 엔디언 표기법  (0) 2020.03.24
[Reversing] 리버스 엔지니어링이란?  (0) 2020.01.04

[System] 어셈블리어 명령

 

시스템 해킹


(1) 어셈블리어 기본 명령

  • CPU 종류에 따라 동작 가능한 작동 코드가 다른 경우도 있지만, 대부분 비슷

 

1-1. 산술 연산 명령

  • 메모리나 범용 레지스터에 위치한 바이트, 워드, 더블 워드 정수에서 기본적 정수 계산

 

ADD (add)

  • 제1피연산자와 제2피연산자 값을 더한 결과 값을 제1피연산자에 저장

형식 ADD  [제1피연산자], [제2피연산자]
사용 예 ADD AL 4
AL이 원래 3이었다면, 명령 실행 후에는 7이 됨

 

 

SUB (Subtract)

  • 제1피연산자에서 제2피연산자 값을 뺀 결과 값을 제1피연산자에 저장

형식 SUB [제1피연산자], [제2피연산자]
사용 예 SUB AL 4
AL이 원래 7이었다면, 명령 실행 후에는 3이 됨

 

 

CMP (Compare)

  • 데이터의 두 값을 비교할 때 사용

  • 예로 'cmp a, b'는 a에서 b를 뺀 값이 0이면 참이 됨

형식 CMP [제1피연산자], [제2피연산자]
사용 예 CMP AL 4
AL이 원래 7이었다면, 명령 실행 후에는 3이 되고 0이 아니므로 거짓이 됨

 

 

기타 산술 연산 명령

  • ADD, SUB, CMP 외 기타 산술 연산 명령은 다음과 같다.

명령 설명
ADC ADd with Carry 캐리를 포함한 덧셈 수행
SBB SuBtraction with Borrow 캐리를 포함한 뺄셈 수행
DEC DECrement 피연산자 내용을 1 감소 시킴
NEG Change Sign 피연산자 2의 보수로 부호를 반전
INC INCrement 피연산자 내용을 1 증가 시킴
AAA ASCII Adjust for Add 덧셈 결과의 AL 값을 UNPACK 10진수로 보정
DAA Decimal Adjust for Add 덧셈 결과의 AL 값을 PACK 10진수로 보정
AAS ASCII Adjust For Subtact 뺄셈 결과의 AL 값을 UNPACK 10진수로 보정
DAS Decimal Adjust for Subtract 뺼셈 결과의 AL 값을 PACK 10진수로 보정
MUL MUltipLy (Unsigned) AX와 피연산자의 곱셈 결과를 AX 또는 DX:AX에 저장
IMUL Integer MULtiply (Signed) 부호화된 곱셈 수행
AAM ASCII Adjust for Multiply 곱셈 결과의 AX 값을 UNPACK 10진수로 보정
DIV DIVide (Unsigned) AX 또는 DX:AX 내용을 피연산자로 나누고, 몫은 AL 또는 AX에 저장한 다음 나머지는 AH 또는 DX에 저장
IDIV Integer DIVide (Signed) 부호화된 나눗셈 수행
AAD ASCII Adjust for Divide 나눗셈 결과 AX 값을 UNPACK 10진수로 보정
CBW Convert Byte to Word AL의 바이트 데이터를 부호 비트를 포함해 AX 워드로 확장
CWD Convert Word to Double word AX 워드 데이터를 부호를 포함해 DX:AX의 더블 워드로 변환

1-2. 데이터 전송 명령

  • 메모리, 범용 레지스터, 세그먼트 레지스터로 참조되는 주소에 들어 있는 데이터 전송

  • 조건 이동, 스택 접근, 데이터 변환 같은 특정 연산도 수행

 

 

MOV (Move)

  • 데이터를 이동할 때 사용

  • 제1피연산자는 데이터 이동의 목적지이고, 제2피연산자는 이동의 대상

  • 제1피연산자는 레지스터와 메모리가 될 수 있고, 제2피연산자는 수치, 레지스터, 메모리 등이 될 수 있음

  • 하지만 메모리에서 메모리로 이동은 불가능

형식 MOV [제1피연산자], [제2피연산자]
사용 예 MOV AX, [BP+8]
BP의 주소에 8 더한 주소에 있는 데이터 값을 AX에 대입

 

[그림 1] MOV 명령을 실행할 때 스택 동작

 

 

위 그림에서 BP의 현재 값이 0x10000004라면, BP+8은 0x1000000C가 된다.

 

그리고 0x1000000C에 있는 값이 1,024이므로 AX에는 1024가 입력된다.

 

 

PUSH (Push)

  • 스택에 데이터를 삽입할 때 사용

형식 PUSH [제1피연산자]

[그림 2] PUSH 명령을 실행할 때 스택 동작

 

위 그림과 같이 스택은 커지고, 스택 포인터는 데이터 크기만큼 감소한다.

 

 

POP (Pop)

  • 스택에서 데이터를 삭제할 때 사용

  • 스택에서 삭제된 명령은 반환 값으로 받아 사용 가능

형식 POP [제1피연산자]

[그림 3] POP 명령을 실행할 때 스택 동작

 

위 그림과 같이 스택 포인터는 삭제하는 데이터 크기만큼 증가한다.

 

 

LEA (Load effective address to register)

  • 데이터 값을 이동할 때 사용하며, MOV와 조금 다름

  • MOV와 LEA 명령의 공통점은 제1피연산자는 데이터 이동의 목적지지만, 제2피연산자에서 추가 연산을 처리하는 방식이 다름

  • 예로 제2피연산자에 'BP+4'가 있을 때 MOV 명령은 'BP+4'를 주소 값 하나로 처리하지만, LEA 명령은 'BP'만 주소 값으로 인식하고 +4는 BP 주소 값의 추가 연산으로 처리

형식 LEA [제1피연산자] [제2피연산자]
사용 예 LEA AX, [BP+4]
BP의 현재 값이 0x10000004라면, MOV 명령처럼 BP+4인 0x10000008의 주소 값을 가져오는 것이 아닌 0x10000004의 주소에 있는 값에 4를 더해 AX에 대입

 

[그림 4] LEA 명령을 실행할 때 스택 동작

 

 

기타 데이터 전송 명령

  • MOV, PUSH, POP, LEA 외 기타 데이터 전송 명령은 다음과 같다.

명령 설명
XCHG Exchange Register /
Memory with Register
첫 번째 피연산자와 두 번째 피연산자를 바꿈
IN Input from AL / AX to Fixed
port
피연산자로 지시한 포트로 AX에 데이터를 입력
OUT Output from AL / AX to
Fixed port
피연산자가 지시한 포트로 AX의 데이터 출력
XLAT Translate byte to AL BX:AL이 지시한 테이블 내용을 AL로 로드
LES Load Pointer to ES LEA 명령과 유사한 방식으로 다른 ES 데이터의 주소 내용을 참조할 때 사용
LAHF Load AH with Flags 플래그 내용을 AH의 특정 비트로 로드
SAHF Store AH into Flags AH의 특정 비트를 플래그 레지스터로 전송
PUSHF Push Flags 플래그 레지스터 내용을 스택에 삽입
POPF Pop Flags 스택에 저장된 플래그 레지스터 값 삭제

1-3. 논리 명령

  • 연산 부호가 논리 연산을 지정

  • 자리 옮김, 논리합(OR), 논리곱(AND), 기호 변환 등이 있음

 

 

AND (And)

  • 대응되는 비트가 둘 다 1일 때만 결과가 1이고, 그 외는 모두 0

형식 AND [제1피연산자], [제2피연산자]
사용 예 AND AX, 10h
AX 값이 0x08h일 때, 이를 2진수로 표현하면 1000이 된다.
0x10h는 1010이므로 명령을 수행한 후 AX 값은 1000이 된다.

 

 

OR (Or)

  •  대응되는 비트 중 하나만 1이어도 결과가 1이고, 둘 다 0일 때만 0이 됨

형식 OR [제1피연산자], [제2피연산자]
사용 예 OR AX, 10h
AX 값이 0x08h일 때, 이를 2진수로 표현하면 1000이 된다.
0x10h는 1010이므로 명령을 수행한 후 AX 값은 1010이 된다.

 

 

XOR (Exclusive Or)

  • 대응되는 비트 중에서 한 비트가 1이고 다른 비트가 0이면 결과가 1이 됨

  • 비트 두 개가 모두 0 또는 1일 때는 0이 됨

형식 XOR [제1피연산자], [제2피연산자]
사용 예 XOR AX, 10h
AX 값이 0x08h일 때, 이를 2진수로 표현하면 1000이 된다.
0x10h는 1010이므로 명령을 수행한 후 AX 값은 0010이 된다.

 

 

NOT (Invert)

  • 피연산자 1의 보수를 구하는 작동 코드로 각 비트를 반전시켜 줌

형식 NOT [제1피연산자]
사용 예 NOT AX
AX 값이 0x08h일 때, 이를 2진수로 표현하면 1000이 된다.
명령을 수행한 후 AX 값은 0111이 된다.

 

 

TEST (And function to flags, no result)

  • CMP 명령처럼 데이터의 두 값을 비교할 때 사용

  • 단 CMP처럼 제1피연산자에서 제2피연산자 값을 빼는 과정이 없음

  • 즉, 데이터 변경 없이 단순 비교만 함

형식 TEST [제1피연산자] [제2피연산자]
사용 예 TEST AL 00001001b
AL 레지스터에 비트 0과 비트 3이 둘아 0인지 확인하고  싶다면, 제2피연산자에 비트 0(오른쪽의 첫 번째 비트)과 비트 3(오른쪽에서 네 번째 비트)에 1을 적고 나머지에는 0을 적은 후 TEST 명령을 실행한다.
AL의 비트 0과 비트 3이 모두 0이라면 ZF가 세트된다.
비트 0과 비트 3 중 하나라도 1이면 ZF는 클리어된다.
AX가 0x08h라면 비트 3이 1이므로 ZF는 클리어된다.

 

 

기타 논리 명령

  • AND, OR, XOR, NOT, TEST 외 기타 논리 명령은 다음과 같다.

명령 설명
SHL / SAL Shift Left / Shift Arithmetic Left 왼쪽으로 피연산자만큼 자리옮김(이동)
SHR / SAR Shift Right / Shift Arithmetic Right 오른쪽으로 피연산자만큼 자리옮김(이동)
ROL Rotate Left 왼쪽으로 피연산자만큼 회전 이동
ROR Rotate Right 오른쪽으로 피연산자만큼 회전 이동
RCL Rotate through Carry Left 자리 올림(Carry)을 포함 왼쪽으로 피연산자만큼 회전 이동
RCP Rotate through Carry Right 자리 올림(Carry)을 포함 오른쪽으로 피연산자만큼 회전 이동

1-4. 스트링 명령

  • 바이트로 구성된 데이터를 메모리에서 가져오거나 메모리로 전송

 

REP (Repeat)

  • ADD나 MOVS 같은 작동 코드 앞에 위치

  • CX가 0이 될 때까지 뒤에 오는 스트링 명령을 반복

형식 REP 작동 코드 -

 

 

MOVS (Move String)

  • 바이트나 워드, 더블 워드를 옮기는 명령으로 각각 MOVSB, MOVSW, MOVSD가 있음

  • DS:SI가 지시한 메모리 데이터를 ES:DI가 지시한 메모리로 전송

  • MOVS는 피연산자 없이 단독으로 쓰며, 다음 REP 등과 함께 사용되기도 함

형식 MOVSB
사용 예                                     CLD
                                    LEA                          SI,                        String_1
                                    LEA                          DI,                        String_2
                                   MOV                         CX,                       384
REP                         MOVSB  
CLD (Clear Direction) : 디렉션 플래그를 지움
LEA SI, String_1 : String 1의 주소 값을 SI (Source Index)에 저장
LEA DI, String_2 : String 2의 주소 값을 DI (Destination Index)에 저장
MOV CX, 384 : CX에 384를 저장
REP MOVSB : SI에서 DI까지 CX가 0이 될 때까지 1바이트씩 복사

 

 

기타 스트링 명령

  • REP, MOVS 외 기타 스트링 명령은 다음과 같다.

명령 설명
CMPS CoMPare String DS:SI와 ES:DI 내용을 비교한 결과에 따라 플래그 설정
SCAS SCAn String AL 또는 AX와 ES:DI가 지시한 메모리 내용을 비교한 결과에 따라 플래그 설정
LODS LOaD String SI 내용을 AL 또는 AX로 로드
STOS STOre String AL 또는 AX를 ES:DI가 지시하는 메모리에 저장

1-5. 제어 전송 명령

  • 점프 (분기), 조건 점프 (조건 분기), 루프, 호출과 리턴 연산 등으로 프로그램 흐름 제어

 

JMP (Unconditional Jump)

  • 대표적인 점프 명령

  • 프로그램 실행 주소 또는 라벨로 이동

형식            JMP                   [제1피연산자]
사용 예            JMP                         100h
주소로 직접 지정한 100h번지로 점프
          String:                       MOV                  CX,                      384
                                                 ...
                                              JMP                  String
라벨로 JMP를 지정하는 경우

 

이외에도 JMP 명령은 조건에 따라 다음과 같이 다양하게 사용된다.

명령 점프 조건 설명
JC Carry Flag Set CF = 1 CF 값이 1이면 점프
JNC Not Carry Flag Set CF = 0 CF 값이 0이면 점프
JE / JZ Equal / Zero ZF = 1 결과가 0이면 점프
JA / JNBE above / Not below Not Equal CF = 0 and ZF = 0 결과가 크면 점프 (부호화되지 않은 수)
JAE / JNB above Or Equal / Not below CF = 0 결과가 크거나 같으면 점프 (부호화되지 않은 수)
JB / JNAE below / Not above Nor below CF = 1 결과가 작으면 점프 (부호화되지 않은 수)
JL / JNGE Less / Not Greater Nor Equal SF != OF 결과가 작으면 점프 (부호화된 수)
JBE / JNA below Or Equal / Not above (CF or ZF) = 1 결과가 작거나 같으면 점프 (부호화되지 않은 수)
JG / JNLE Greater / Not Less Nor Equal ZF = 0 and SF = OF 결과가 크면 점프 (부호화되지 않은 수)
JGE / JNL Greater Or Equal / Not Less SF = OF 결과가 크거나 같으면 점프 (부호화된 수)
JLE / JNG Less Or Eqal / Not Greater ZF = 1 or SF != OF 결과가 작거나 같으면 점프 (부호화되지 않은 수)
JNE / JNZ Not Equal / Not Zero ZF = 0 결과가 0이 아니면 점프
JNO Not Overflow OF = 0 오버플로가 아니면 점프
JNP / JPO Not Parity / Parity Odd PF = 0 PF가 1이면 점프
JNS Not sign SF = 0 SF가 1이면 점프
JO Overflow OF = 1 오버플로 발생 시 점프
JP / JPE Parity / Parity Even PF = 1 PF가 1이면 점프
JS Sign SF = 1 SF가 1이면 점프
JCXZ CX Zero CX = 0 CX가 0이면 점프

 

  • above, below : 부호 없는 두 수의 크기 관계

  • Greater, Less : 부호 있는 두 수의 크기 관계

 

 

CALL (Call)

  • JMP처럼 함수 호출 시에 사용하며, 제1피연산자에 라벨 지정

  • 하지만 CALL은 리턴 주소로 IP 주소를 백업하여 'PUSH + EIP + JMP'와 의미가 같음

형식 CALL [제1피연산자]

 

 

RET (Return from CALL)

  • 함수에서 호출한 곳으로 돌아갈 때 사용하는 명령

  • 'POP EIP'와 의미가 같음

 

다음은 CALL과 RET의 예시이며, AX에는 0x08h 값이 저장되었다고 가정한다.

                                                    CALL                              SUBR
                                                    ADD                                  AX,                           10h
                                                      ...
              SUBR :                         INC                                   AX
                                                      ...
                                                    RET

 

위 에에서는 SUBR 함수를 호출하면, SUBR 라벨이 있는 곳에서 RET까지 실행된다.

 

이때 INC AX가 있으므로 AX는 0x09h가 된다.

 

RET 명령이 실행되면 CALL 다음 줄인 'ADD AX, 10h'를 실행한다.

 

따라서 AX는 0x19h가 될 것이다.

 

 

LOOP (Loop CX times)

  • 문장들의 블록을 지정된 횟수만큼 반복

  • CX는 자동으로 카운터로 사용하며, 루프를 반복할 때마다 감소

형식 LOOP [제1피연산자]
사용 예                               MOV                   AX,                   0
                              MOV                   CX,                   5
         L1 :               INT                    INT                  AX
                              LOOP                  L1
제1피연산자는 라벨이 되며, 예에서 L1이 CX 숫자만큼 다섯 번 회전하므로 결과적으로 AX는 5가 된다.

 

 

INT (Interupt)

  • 인터럽트가 호출되면 CS:IP와 플래그를 스택에 저장하고, 그 인터럽트와 관련한 서브 루틴 실행

  • 제1피연산자는 인터럽트의 번지로 00~FF(256개) 번지가 할당되어 있고, INT 10H와 INT 21H 인터럽트 많이 사용

  • INT 10H는 화면 클리어 기능, 커서 위치 설정, 커서 현재 위치 찾기 기능이 있음

  • INT 21H는 모니터에 문자 출력, 키보드에서 문자열 데이터 입력 기능이 있음

형식 INT [제1피연산자]

 

 

 

기타 제어 전송 명령

명령 설명
INTO INTerrupt on Overflow 오버플로 발생 시 인터럽트 실행
IRET Interrupt RETurn 인터럽트에서 복귀 (리턴)
LOOPZ / LOOPE LOOP while Zero / Equal 제로 플래그가 1이고 CX 값이 0보다 크면 루프 계속 수행
LOOPNZ /
LOOPNE
LOOP while Not Zero /
Not Equal
제로 플래그가 0이고 CX 값이 0보다 크면 루프 계속 수행

1-6. 프로세서 제어 명령

  • 레지스터의 플래그 값을 세트하거나 클리어

 

STC (Set Carry)

  • 피연산자 없이 사용

  • EFLAGS 레지스터의 CF 값을 세트

 

NOP (No Operation)

  • 아무 의미 없는 명령

  • 일종의 빈칸을 채우는 데 사용

 

기타 프로세서 제어 명령

  • 기타 프로세서 제어 명령은 다음과 같다.

명령 설명
CLC CLear Carry 캐리 플래그 클리어
CMC CoMplement Carry 캐리 플래그 반전
HLT HaLT 정지
CLD CLear Direction 디렉션 플래그 클리어
CLI CLear Interrupt 인터럽트 플래그 클리어
STD SeT Direction 디렉션 플래그 세트
STI SeT Interrupt 인터럽트 인에이블 플래그를 세트
WAIT WAIT 프로세스 일시 정지 상태
ESC ESCape to External Device 종료 명령

# Reference

 

https://www.hanbit.co.kr/store/books/look.php?p_code=B3283906872

[System] 어셈블리어 구조 & 주소

 

시스템 해킹


(1) 어셈블리어의 구조

  • 어셈블리어에는 Intel과 AT&T 문법이 존재

  • 윈도우는 Intel 문법, 리눅스는 AT&T 문법 주로 사용

  • 두 문법의 차이점은 제1피연산자와 제2피연산자의 위치

  • Intel 문법은 목적지가 앞에 오고 원본이 뒤에 오지만, AT&T 문법은 반대

 

1-1. 어셈블리어 명령 형식 (Intel 문법)

  • Label  : (라벨)

  • MOV (작동 코드)

  • AX (제1피연산자)

  • BX (제2피연산자)

  • ; comment (설명)

 

MOV는 제2피연산자 값을 제1피연산자로 복사하는 명령이다.

 

라벨은 기계어 코드로 직접 번역하지 않고, 점프(분기) 명령 등에서 참조할 때 메모리 주소를 계산하는 데 사용한다.

 

사용법은 다음과 같다.

Label_1 : MOV AX, BX
         	...
         	...
         	JMP Label_1 (Label_1로 무조건 이동하라는 명령)

 

다음 코드는 MOV부터가 기계어로 번역되는 부분으로 어셈블리어 작동 코드(명령 코드)라고 한다.

 

그리고 피연산자는 레지스터, 문자, 숫자, 메모리 주소가 될 수 있다.


(2) 어셈블리어 데이터 타입과 리틀 엔디안 방식

2-1. 데이터 타입

  • 바이트(Byte) : 1바이트(8비트) 데이터 항목

  • 워드(word) : 2바이트(16비트) 데이터 항목

  • 더블 워드(Double word) : 4바이트(32비트) 데이터 항목

[그림 1] 어셈블리어 데이터 타입

 

 

2-2. 리틀 엔디안 방식

  • 데이터는 리틀 엔디안 방식에 따라 스택에 저장, 참조

  • 번지 두 개로 나누어 저장해야 하는 16비트 데이터의 경우 하위 바이트는 하위 번지 저장, 상위 바이트는 상위 번지 저장

  • 예로 HEX 값 0x34F3을 1500번지에 저장하기 위해 하위 값 0xF3은 1500번지에 저장하고, 상위 값 0x34는 1501번지에 저장 

주소 1500번지 1501번지
저장된 데이터 F3 34

[그림 2] 어셈블리어 데이터 타입별 메모리 저장 위치

 

2-3. 빅 엔디안 vs 리틀 엔디안

  • 메모리에 데이터를 저장하는 바이트 순서를 바이트 정렬이라 함

  • RISC 칩을 기반으로 한 IBM 컴퓨터 등은 빅 엔디안 방식 사용

  • 0x86, DEC 시스템 등은 리틀 엔디안 방식 사용

  • 빅 엔디안은 최상위 바이트(MSB)부터 차례로 저장하는 방식, 리틀 엔디안은 최하위 바이트(LSB)부터 차례로 저장하는 방식


(3) 어셈블리어 주소 지정 방식

  • 80x86 시스템에서는 메모리에 접근할 수 있도록 다양한 주소 지정 방식 제공

 

3-1. 레지스터 주소 지정

  • 일반적으로 생각하는 메모리에 접근하는 방식은 아님

  • 레지스터 주소 값을 직접 지정하여 복사하는 방식으로 처리 속도가 가장 빠름

 

다음 내용은 BX 레지스터 내용을 DX 레지스터로 복사하는 명령의 예이다.

MOV DX, BX

 

3-2. 직접 메모리 주소 지정

  • 가장 일반적인 주소 지정 방식

  • 피연산자 하나가 메모리 위치를 참조하고, 다른 하나는 레지스터를 참조

  • 두 피연산자가 메모리를 직접 참조하는 것은 MOVS와 CMPS만 가능

 

다음 내용은 메모리 주소를 레지스터에 복사하고 레지스터를 메모리에 저장하는 두 가지 명령의 예이다.

MOV AL, DS:[8088h]
MOV DS:[1234h], DL

 

예에서 DS:[8088h]와 DS:[1234h]는 각각 '세그먼트:오프셋' 형식으로 메모리에 직접 접근하는 방식이다.

 

[그림 3] 직접 메모리 주소 지정 예

 

위 그림과 같이 각각 메모리에 접근한다.

 

 

3-3. 레지스터 간접 주소 지정

  • 직접 메모리 주소 방식처럼 '세그먼트:오프셋' 형식 사용

  • 단지 세그먼트를 명시적으로 적지 않음

MOV AL, [BX]
MOV AL, [BP]

[BX]와 [BP]는 실제로는 DS:BX, SS:BP 주소를 가리킨다.

 

[그림 4] 레지스터 간접 주소 지정 예

 

 

그리고 다음과 같이 기본이 아닌 세그먼트를 강제로 지정할 수도 있다.

MOV AL, CS:[BX]
MOV AL, DS:[BP]

 

3-4. 인덱스 주소 지정

  • 레지스터 간접 지정 방식에 변위가 더해진 메모리 주소 지정 방식

 

다음은 20h만큼 더해 메모리를 참조한 명령의 예이다.

MOV AL, [BX+20h]
MOV AL, [BP+20h]

 

위 예에서 [BX+20h]와 [BP+20h]는 DS:[BX+20h], SS:[BP+20h] 주소를 가리킨다.

 

[그림 5] 인덱스 주소 지정 예

 

위의 예는 다음과 같이 바꾸어 표현할 수 있다.

MOV AL, 20h[BX]
MOV AL, 20h[BP]

 

 

3-5. 베이스 인덱스 주소 지정

  • 실제 주소를 생성하기 위해 베이스 레지스터(BX 또는 BP)와 인덱스 레지스터(DI 또는 SI)를 결합

 

다음 예처럼 베이스 레지스터(BX 또는 BP)와 인덱스 레지스터(DI 또는 SI)를 결합한다.

MOV AL, [BX+SI]
MOV AL, [BP+SI]

 

[그림 6] 베이스 인덱스 주소 지정 예

 

 

베이스 인덱스 주소 지정도 인덱스 주소 지정처럼 다음과 같이 바꾸어 표현이 가능하다.

MOV AL, [BX][SI]
MOV AL, [BP][SI]

 

베이스 인덱스 주소 지정은 보통 2차원 배열 주소를 지정할 때 사용한다.

 

 

 

3-6. 변위를 갖는 베이스 인덱스 주소 지정

  • 베이스-인덱스 변형으로 실제 주소를 생성하려고 베이스 레지스터, 인덱스 레지스터, 변위를 결합

 

다음과 같이 표현할 수 있다.

MOV AL, [BX+SI+20h]
MOV AL, [BP+SI+20h]

[그림 7] 변위를 갖는 베이스 인덱스 주소 지정 예

 

 

변위를 갖는 베이스 인덱스 주소 지정 방식도 인덱스 주소 지정 방식처럼 다음과 같이 바꾸어 표현 가능하다.

MOV AL, [BX][SI][20h]
MOV AL, [BP][SI][20h]

# Reference

 

https://www.hanbit.co.kr/store/books/look.php?p_code=B3283906872

+ Recent posts