어셈블리언어에서 사용되는 산술 연산자와 비트 연산자에 대해 학습한 내용입니다.

어셈블리 언어 - 산술, 비트 연산자

문법

→ 어셈블리 언어에도 다른 프로그래밍 언어와 같이 문법이 존재합니다. 물론 다른 프로그래밍 언어보다 매우 단순하며 한 줄에 하나의 연산만 수행할 수 있다는 특징이 있습니다.

기본 구문

;label/address mnemonic operand ;comment
00A92DF9 mov eax,dword ptr [0AD4194] ;moves the dword value at 0AD4194 to
  1. 레이블(label)

    → 레이블은 명령어 코드 위치를 정의하는 데 사용됩니다. 일반적으로 메모리에 코드가 배치될 주소를 정확히 알지 못하는 상황에서 어셈블리 코드를 개발할 때 사용됩니다.

  2. 주소(address)

    → 해당 명령어가 위치해 있는 메모리 상 주소를 나타냅니다.

  3. 니모닉(mnemonic)

    → 니모닉은 MOV, ADD, SUB와 같이 사람이 읽을 수 있는 형상의 명령어를 뜻합니다. 연산자를 나타냅니다. 이러한 니모닉은 opcode라고 불리는 한 바이트 명령어 또는 여러 바이트 명령어로 표현됩니다.

  4. 피연산자(operand)

    → 말 그대로 연산의 대상이 되는 피연산자 입니다. 명령어의 인자라고 할 수 있는 피연산자는 보통 목적지와 출발지로 읽습니다. 보기와 같은 명령어에서 dword ptr [0AD4194], 즉, 0AD4194에 위치한 데이터를 eax 레지스터로 옮긴 것입니다. 그러므로 eax는 목적지, 0AD4194는 출발지가 됩니다.

  5. 주석(comment)

    → 해당 라인에 대한 부연 설명을 적을 수 있는 주석 기능 입니다. 일반적으로 우리는 프로그램의 모든 명령어 라인에 주석을 추가할 수 있습니다. 어셈블리 언어에서 주석은 세미콜론(;)으로 표시됩니다.

명령어

명령어 종류

  • 데이터 복사 및 액세스 명령어

    → MOV, LEA, MOVB 등

  • 산술 명령어

    → ADD, SUB, MUL, DIV 등

  • 이진 논리 명령어

    → XOR, NOT, SUR, ROL 등

  • 흐름 제어

    → JMP, CAL, CMP, INT 등

데이터 복사 및 액세스 명령어

(1) MOV

→ MOV 연산자는 데이터를 이동시키는 역할을 수행합니다.

  • MOV의 피연산자

    → MOV는 레지스터, 상수, 특정 주소를 피연산자로 둘 수 있습니다. 하지만 상수로의 데이터 이동은 불가능합니다.

      ;예시
      MOV AL, BYTE PTR[00000071] ; AL에 00000071번지의 데이터를 복사합니다.
      MOV EAX, EDX               ; EDX 레지스터에 들어있는 데이터를 EAX로 복사합니다.
      MOV EAX, 0xAABBCCDD        ; 앞서 보여드렸듯이 AABBCCDD의 16진수 상수를 EAX로 복사합니다.
    

(2) LEA

→ LEA 연산자는 Load Effect Address의 약자로 주소를 가져오는 데 사용됩니다. MOV 연산이 지정된 주소에서 값을 읽는 데 사용된다면 LEA는 주소 자체를 가져온다고 생각하시면 됩니다. 코드로 보여드리겠습니다.

MOV EAX, DWORD PTR [00000060]    ; 00000060 번지에 있는 값을 EAX 레지스터로 가져옵니다.
LEA EAX, DWORD PTR [00000060]    ; 60h를 EAX 레지스터로 가져옵니다.

이러한 LEA 연산자는 주소 계산을 훨씬 용이하게 해준다는 이점이 있습니다. C언어에서의 & 역할을 해준다고 생각하시면 편합니다. []은 주소라는 의미이다.

(3) LEA 연산의 이로운 예

; using MUL and ADD
mov ecx, 1111h
mov ebx, 2222h
mov eax, 2    ; eax = 2
mul ecx       ; eax = 2222h
add eax, ebx  ; eax = 4444h
add eax, 1    ; eax = 4445h

; using LEA
mov ecx, 1111h
mov ebx, 2222h
lea eax, [ecx*2+ebx+1] ; eax = 4445h

산술 연산

→ 여기서 저희가 알고가야 할 것은 x86 기반은 CISC를 기반으로 하기 때문에 여러 산술 연산자와 같은 명령어는 그 뒤에 더 저수준의 연산 집합을 가지고 있습니다. 산술 연산은 작업 중에 충족해야 할 상태를 플래그의 도움을 받아 참조하여 작동합니다.

덧셈과 뺄셈

주로 사용되는 플래그

→ OF, SF, CF가 주로 사용됩니다.

  • OF → 연산 시 Overflow 발생 여부를 체크합니다. ( 발생 시 = 1 )
  • SF → 연산 시 Sign bit의 상태를 체크합니다. ( 음수일 경우 = 1 )
  • CF → 연산 시 CARRY가 발생했는지 체크합니다. ( 발생 시 = 1 )

ADD

→ ADD 연산자는 말 그대로 덧셈을 수행하는 어셈블리 연산자입니다.

mov eax, 1111h    ; eax에 0x1111을 넣는다.
add eax, 2222h    ; eax += 2222h 와 동일한 명령어이다.

결과: EAX → 0x00003333

SUB

→ SUB 연산자는 뺄셈에 사용되는 어셈블리 연산자이다.

mov eax, 1111h    ; eax에 0x1111을 넣어준다.
sub eax, 111h     ; eax -= 111h와 같은 명령어이다.

결과: EAX → 0x00001000

++와 - -

→ 어셈블리 언어에서도 하나의 피연산자만으로 가능한 덧셈 뺄셈 연산이 있습니다. C언어에서의 전위, 후위 연산자와 동일한 수행을 해줍니다. 하지만, 전위와 후위처럼 앞에 오거나 뒤에 오거나 할 것 없이 해당 명령어에서 피연산자의 값에 1을 더해주거나 빼줍니다.

INC

→ 단순히 피연산자의 값에 1을 추가해주는 연산자입니다.

mov eax, 0xffffffff    ; eax에 0xffffffff 값을 넣어줍니다.
inc eax                ; eax에 +1을 추가해줍니다.

결과: EAX → 0

DEC

→ 단순히 피연산자의 값에 1을 감소시키는 연산자입니다.

mov eax, 0             ; eax에 0x00000000 값을 넣어줍니다.
dec eax                ; eax에 1을 감소시킵니다.

결과: EAX → 0xffffffff

곱셈과 나눗셈

MUL

→ 어셈블리 언어에서 곱셈을 담당하는 연산자입니다. 결과값이 레지스터의 용량을 초과할 경우 해당 값들은 EDX 레지스터를 빌려 저장됩니다.

mov eax, 0x80000000    ; eax에 0x80000000 값을 넣습니다.
mov ecx, 2             ; ecx에 곱해줄 값을 넣습니다. 2
mul ecx                ; 기본적으로 eax에 있는 값과 피연산자를 곱해줍니다.

결과: EAX → 0x00000000 EDX → 0x00000001 CF → 1 OF → 1

IMUL

→ 다른 레지스터나 주소에 대한 피연산자를 허용하는 곱셈입니다.

DIV

→ 어셈블리 언어에서 나눗셈을 담당하는 연산자입니다. 나누기 작업 후 몫이 AL, AX 또는 EAX에 저장되며, 나머지는 AH, DX 또는 EDX에 저장됩니다.

IDIV

→ IMUL과 마찬가지로 다른 레지스터나 주소에 대한 피연산자를 허용하는 나눗셈입니다.

그 외 연산자들

NEG

→ 2의 보수를 구하는 연산자입니다.

neg eax
neg dword ptr [00403000]

MOVSX

→ 부호를 포함, BYTE → WORD, WORD → DWORD로 이동시킵니다. 피연산자를 수용하기 때문에 CBW, CWDE, CWD 보다 유연한 연산자입니다.

mov bx, 0xffff    ; bx에 ffffh 값을 넣습니다.
movsx eax, bx     ; eax로 확장합니다.

결과: EAX → 0xffffffff (-1)

CBW

→ MOVSX와 유사, 부호를 포함해 BYTE를 WORD로 변환하는데 AL과 AX만 무조건 영향을 받습니다. 그러므로 피연산자가 존재하지 않는 연산자입니다. 즉, 바이트 크기인 AL이 워드 크기 AX로 확장되는 것이며 이러한 변환은 ‘- >’ 기호를 이용합니다. AL - > AX는 저장된 값을 손상시키지 않고 1byte의 숫자를 2byte로 확장한다는 것을 뜻 합니다.

CWDE

→ WORD를 DWORD로 변환합니다. AX - > EAX

CWD

→ WORD를 DWORD로 변환하는 점에서 CWDE와 같지만 CWD는 AX - > DX:AX 에 영향을 미칩니다.

비트 대수

→ 일반적으로 암호학의 암호화와 복호화에 사용됩니다.

NOT

→ 비트 반전을 수행합니다.

AND

→ 비교되는 bit가 둘 다 1인 경우 bit를 1로 설정합니다. 그렇지 않은 경우 0으로 설정합니다.

OR

→ 비교되는 bit가 한 쪽만 1이더라도 bit를 1로 설정합니다. 둘 다 0이면 0으로 설정합니다.

XOR

→ 두 bit가 같은 경우 1이든 0이든 0으로 설정합니다. 두 bit가 다른 경우 1로 설정합니다. 주로 레지스터 초기화에 많이 사용됩니다.

xor eax, eax    ; eax를 0으로 초기화

SHL/SAL

→ bit를 왼쪽으로 이동(shift) 시킵니다.

SHR/SAR

→ bit를 오른쪽으로 이동시킵니다.

ROL

→ bit를 왼쪽으로 회전(rotate)시킵니다.

ROR

→ bit를 오른쪽으로 회전시킵니다.

맨 위로 이동 ↑

카테고리:

업데이트: