캡스톤 디자인 #5 어셈블리어

llip·2022년 5월 15일
0

캡스톤 디자인

목록 보기
5/9

디컴파일러를 통해 IR 코드로 변환한 내용을 읽어볼 수 있기 위해 어셈블리어를 공부하기로 했다.

정의

기계어는 기계에 친화적인 언어인만큼 컴퓨터가 바로 인식할 수 있는데, 그 때문에 실행 속도가 매우 빠르다. 그러나 인간이 읽기 어려워 배우기도 어렵고 유지보수도 힘들다. 따라서 컴퓨터가 바로 읽을 수 있는 이진수의 기계어를 인간이 읽을 수 있게끔 보완한 어셈블리어가 만들어졌다.

어셈블리어는 기계어와 1:1 대응이 가능한 컴퓨터 프로그래밍에서의 저급 언어이다.

어셈블리어를 기계어로 변환해주는 것을 어셈블러(assembler)라고 하고, 기계어를 어셈블리어로 변환하는 것을 디스어셈블러(disassembler)라고 한다.
어셈블러와 디스어셈블러는 기계어와 어셈블리어 명령어를 1:1로 대응시켜준다.

특징

컴퓨터 아키텍처마다 사용하는 기계어가 다르므로, 기계어를 변환한 어셈블리어도 각기 조금씩 다르다. 오늘날은 하나의 프로세서 계열에 기반을 두고 있다.

어셈블러에는 4가지 종류가 있다.

MASM : 윈도우
GAS : 리눅스/유닉스
NASM : 윈도우/리눅스/맥
SASM : 윈도우/리눅스

장점

같은 종류의 프로세서만 실행할 수 있고 사전 지식이 꽤 필요하다. 여러가지 구성 요소들을 직접 다룰 수 있다는 장점이 있다.

또한 컴퓨터의 실행 과정을 이해하기 쉽고, 성능에 최적인 프로그램의 작성이 가능하다.

명령어 수행 루틴

Operand: 피연산자
Opcode: 명령어

표기 방식은 문법에 따라 차이가 나는데, Intel 문법과 AT&T 문법이 있다.
Intel 방식에서 ADD EAX EBX라는 구분을 해석하면 EBX의 값을 EAX에 더하는 것이다.
AT&T 방식에서는 EAX의 값을 EBX에 더한다는 뜻이다.

숫자 표기

Intel : 숫자를 그대로 사용한다. (1, 2, ... )
AT&T : 숫자의 앞에 S를 붙여 사용한다. (S1, S2, ... )

레지스터 표기

Intel : 레지스터의 명칭을 그대로 사용한다.
AT&T : 레지스터의 앞에 %를 붙여 사용한다.

평균적으로는 Intel 문법을 따라간다.


용어정리

[ 레지스터 ]

RAX : 64비트 단위의 레지스터
EAX : 32비트 단위의 레지스터. 곱셈과 나눗셈 명령어에서 자동으로 사용된다.
AX : 16비트 단위의 레지스터
AH, AL : 8비트단위의 레지스터

ECX : CPU에 의하여 자동으로 루프카운터 사용
EBP : 고급 수준의 프로그래밍이 아니라면 일반 계산과 데이터 전송에서 사용 금지. 고급언어의 스택에 있는 함수 매개 변수와 지역 변수를 참조하기 위해 사용.
EFLAGS : CPU의 동작 제어, CPU 연산의 결과 반영하는 개별적 2진수 비트들로 구성.

범용 레지스터

  • 32비트: ESI, EDI, EBP, ESP
  • 16비트: SI, DI, BP, SP

인덱스 레지스터

  • ESI : 복사나 비교 시 출발지 주소를 저장하는 레지스터
  • EDI : 복사나 비교 시 목적지 주소를 저장하는 레지스터. 고속 메모리 전송 명령어에 사용된다.

포인터 레지스터

  • EIP : 다음에 실행할 명령어의 주소 가짐. 현재 실행 명령어가 종료되면 레지스터에 있는 명령어를 실행하게 됨. 이 레지스터를 조작해서 프로그램을 새 위치로 분기하기도 함.
  • ESP : 스택 포인터의 가장 최근에 저장된 공간의 주소를 저장하는 레지스터. 보통 계산 및 데이터 전송에 거의 사용하지 않음.
  • EBP : 스택 포인터의 기준점을 저장하는 레지스터

플래그 레지스터

  • CF (캐리 플래그) : 연산 결과 최상위 비트로부터 높은 자리로 자리 올림이나 최상위 비트로부터 빌림이 발생한 경우에 1로 세팅된다. 그 외의 경우는 0으로 리셋. (프로그램에 의해서도 세팅, 리셋 가능.)
  • PF (패리티 플래그) : 연산 결과 1로 된 비트수가 짝수면 1, 홀수면 0으로 리셋.
  • AF (보조 캐리 플래그) : 8(16)비트의 연산에서 하위 4(8)비트로부터 상위 4(8)비트로 자리올림/빌림이 발생한 경우 1로 세팅. 그 외의 경우 0 세팅. (10진 보정 명령에 있어서도 사용 가능.)
  • ZF (제로 플래그) : 연산 결과 0으로 되었을 때 1로 세팅, 그 외의 경우 0으로 리셋.
  • SF (사인 플래그) : 연산 결과 최상위 비트가 1일 때(보수 표현으로 음수가 될 때) 1로 세팅, 그 이외에 0으로 리셋.
    - OF (오버 플로우 플래그) : 연산을 부호 달린 숫자로 했을 때 오버플로/언더플로가 발생한 경우 1로 세팅, 그 외 0으로 리셋.
  • DF (디렉션 플래그) : 스트링 조작시 플래그가 0이면 번지를 나타내는 레지스터 값이 자동 증가, 1이면 자동 감소.
  • IF (인터럽트 플래그) : 0이면 INTR 단자로부터 외부 인터럽트 요구 무시, 1이면 외부 인터럽트 요구 받아들임.
  • TF (트랩 플래그) : 0이면 CPU는 평상시처럼 명령을 실행한다. 1이면 CPU는 한 명령을 실행할 때마다 자동적으로 내부 인터럽트를 발생하고 인터럽트 처리 루틴으로 들어감. (처리루틴 실행 중에 TF는 0으로 클리어.) 프로그램 추적에 사용.

시스템 레지스터

  • TR (태스크 레지스터) : 현재 실행 중인 태스크의 상태 세그먼트 주소 포함.
  • CR (제어 레지스터) : 태스크 전환/페이징/캐시 메모리 활성화 등 시스템 수준의 동작을 제어하는 상태 플래그와 데이터 필드 포함.
  • IDTR (인터럽트 서술자 테이블 레지스터) : IDT의 주소를 포함한다. 테이블은 인터럽트를 처리하는 방법을 제공한다.
  • GDTR (전역 서술자 테이블 레지스터) : GDT의 주소를 포함한다. 테이블은 TSS와 LDT에 대한 포인터 포함.
  • LDTR (지역 서술자 테이블 레지스터) : 현재 실행중인 프로그램의 코드/데이터/스택 에 대한 포인터 포함.
  • DR (디버그 레지스터) : 디버깅 시 프로그램이 brake point를 설정함.
  • MSR (모델 특정 레지스터) : 성능 모니터링과 기계 구조의 확인같은 운영체제의 스템 작업에 사용한다. 용도는 IA-32 프로세서의 종류에 따라 변함.

명령어

[변수 선언]

변수 : 메모리의 시작주소와 사용되는 메모리의 크기
변수의 3가지 속성 (시작 주소, 저장된 값, 사용하는 데이터의 크기)

사용할 메모리의 크기는 프로그래머가 결정하지만, 사용할 메모리의 위치는 어셈블러가 변환할 때 결정된다.

a(변수이름) 변수형태 b(변수의 초깃값 혹은 크기와 개수)

a는 main 메모리의 stack을 빌려서 저장한다.
이런 변수들은 Input을 통해 값이 설정된다.

[ 변수 형태 ]

resb 형태 : a변수에 b byte 크기의, b개의 메모리 공간을 확보하겠다.
dw 형태 : b가 0x33일 경우, a의 이름으로 1바이트 1개의 메모리 공간을 확보하며 초깃값은 0x33이다.

바이트 변수

db : 1바이트 크기의 변수를 표현한다. 편수의 크기는 db로 표시. 초깃값 설정 시 필요가 없다면 ?로 표기한다.

워드 변수

dw : 2바이트 크기의 변수를 표현한다. 변수의 크기는 dw로 표시. 가장 기본이 되는 변수이다.

이 외에도 dd (double 변수), dq(quad 변수), dt(10 바이트 변수) 등이 있다.

배열

배열이름 변수형태 값1, 값2, 값3

[ 레지스터나 메모리 값 출력 ]

레지스터나 메모리에 있는 값을 화면에 출력하기 위해 사용하는 명령어이다.

PRINT_HEX 바이트수, 레지스터/변수이름 : 16진수 출력
PRINT_DEC 바이트수, 레지스터/변수이름 : 10진수 출력
PRINT_STRING "문자열/변수이름" : 문자열 출력
NEWLINE : 화면의 줄 변경


[ 데이터 ]

mov (데이터 이동)

mov a, b

mov 명령어 : 메모리에 데이터를 보낸다.

  • b의 값을 a에 이동시킨다. 만약 b의 값이 0이라면 초기화를 시키는 것이다.
  • 주소가 가르키는 곳의 값은 [변수이름]으로 표기.
  • ex) move ax, [a] :: ax에 a의 주소값을 보낼 것이다.
  • ex) move [b], ax :: ax에 저장된 값을 b의 주소값에 넣을 것이다.

GET_DEC a, [b]

INPUT에서 받아온 입력값을 a byte의 크기의 10진수 변수 b로 받아온다.

[ 데이터 조작 ]

inc

inc a

a(오퍼랜드)의 값을 1 증가한다.

dec

dec a

a(오퍼랜드)의 값을 1 감소한다.

[ 논리 연산 ]

덧셈

add

add a, b

a의 값에 b의 값을 더해준다.

뺄셈

sub

sub a, b

레지스터나 메모리의 값을 뺄 때 사용한다.

곱셈

imul

imul a, b

a의 값에 b의 값을 곱해준다.

[ 스택 조작 ]

push

push a

a(오퍼랜드)의 값을 스택에 저장한다.

pop

pop a

스택 최상단 값을 꺼내어 a에 저장한다.

[ 프로시저 ]

ret

ret

호출했던 바로 다음 지점으로 이동한다.

call

call proc

프로시저를 호출한다.

[ 분기 ]

jmp

jmp proc

특정한 곳으로 분기한다.

[ 인터럽트 ]

int

int S0x80

OS에 할당된 인터럽트 영역을 system call 한다.

[ 비교 ]

cmp

cmp a, b

레지스 a와 레지스터 b의 값을 비교한다.

[ 컨트롤 레지스터 ]

call

프로시저를 호출한다.

JE/JZ

결과가 0이면 분기한다. (같으면 분기.)

JNE

결과가 0이 아니면 분기한다. (같지 않으면 분기.)

JB/JNAE

결과가 작으면 분기 (부호화 안된 수)

JL/JNGE

결과가 작으면 분기 (부호화 된 수)

JBE/JNA

결과가 작거나 같으면 분기 (부호화 안 된 수)

JNLE/JG

결과가 크면 분기 (부호화 된 수)

JNL/JGE

결과가 크거나 같으면 분기 (부호화 된 수)

JNBE/JA

결과가 크면 분기 (부호화 안 된 수)

[ 기타 ]

XCHG

첫 번째 오퍼랜드와 두 번째 오퍼랜드를 교환한다.

IN

오퍼랜드로 지시된 포트로부터 AX에 데이터를 입력한다.

OUT

오퍼랜드가 지시한 포트로 AX의 데이터를 출력한다.

XLAT

BX:AL이 지시한 테이블의 내용을 AL로 로드한다.

LEA

메모리의 오프셋값을 레지스터로 로드한다.

LDS

DS로 포인터를 불러온다.

LES

ES로 포인터를 불러온다.

LAHF

플래그의 내용을 AH의 특정 비트로 로드한다.

SAHF

AH의 특정 비트가 플래그 레지스터로 전송한다.

PUSHF

플래그 레지스터의 내용을 스택에 쌓는다.

POPF

스택으로부터 플래그 레지스터로 뽑는다.

nop

nop

아무런 동작도 하지 않는다.


이 외의 자세한 명령어는 이쪽을 참고하면 좋을 것 같다.
https://coding-factory.tistory.com/650


참고

https://m.blog.naver.com/sol9501/70087101257

https://lucete1230-cyberpolice.tistory.com/40?category=852249

https://coding-factory.tistory.com/651

profile
공부중

0개의 댓글