정보통신 IT/info

어셈블리어-MASM(Microsoft Macro Assembler)

Jobs 9 2020. 11. 23. 08:15
반응형

MASM(Microsoft Macro Assembler) : 저급 언어

 

어셈블리어의 장점

  • 소량의 코드로 작성하는 경제적인 프로그램(임베디드 시스템 등)
  • 실행 코드의 실행 속도를 정확히 측정 가능
  • 소프트웨어 최적화 및 컴퓨터 구조의 이해 가능

 

문자 표현

  • 따옴표로 둘러싸인 한 문(문자 상수) 또는 문자열(문자열 상수)
  • 문자(열)을 2진 ASCII 코드로 메모리에 저장
  • 일반적인 문자열은 Null Byte로 끝남("Gyeori World",0)

 

예약어 : 특별한 목적을 가지고 사전에 저장된 것

  • 명령어 니모닉 : mov, add, mul 등
  • 레지스터 이름
  • 디렉티브(Directive) : 어셈블러에게 실행 방법을 지시
  • 속성 : 변수와 피연산자의 크기와 사용 정보를 제공(Byte, Word ...)
  • 연산자 : 수식에서 사용
  • 미리 정의된 기호 : 어셈블할 때 상수 정수 값을 반환하는 @data와 같은 기호

 

식별자 : 프로그래머가 선택한 이름으로, 변수/상수/프로시저/코드 레이블 등에 사용됨

  • 1~247개 사이의 문자를 사용할 수 있고 대소문자를 구분하지 않음
  • 어셈블러의 예악어와 같을 수 없음
  • 첫번째 문자는 알파벳(A~Z,a~z), 밑줄(_), @, ?, $이어야 하며(숫자 X), 이후에는 숫자를 사용 가능
  • @는 어셈블러가 미리 정의된 기호의 접두사로 많이 사용되므로 피하는 것이 좋음

 

디렉티브 : 어셈블러가 인식하여 그에 따라 동작하는 소스 코드에 포함되는 명령어(지시 역할)

  • 실행 시간에 실행되지 않음
  • 변수, 매크로, 프로시저를 정의할 수 있음
  • 메모리 세그먼트에 이름을 부여하고 어셈블러와 관련된 많은 기타 관리 작업을 수행
  • .code ~ ; .data ~ ; .code ~; 형태로 병행하여 여러 번 사용 가능
  • DATA 디렉티브(.data) : 변수를 포함하는 프로그램 영역을 표시함
    • DATA?(.data?) : 비초기화 데이터를 선언(상황에 따라 컴파일된 프로그램의 크기를 줄여줌)
  • CODE 디렉티브(.code) : 실행 가능한 명령어를 포함하는 영역을 표시함
    • 분기나 루프 명령어의 목적지로 사용됨
    • 명령어가 위치한 프로그램의 코드 영역에 있는 레이블은 콜론(:)으로 끝나야 함
  • STACK 디렉티브(.stack) : 스택의 크기를 설정하면서(.stack 4096 등) 실행 스택을 가진 프로그램 영역을 표시함
  • INCLUDE 디렉티브 : 필요한 정의와 설정 정보를 지시
  • PROC 디렉티브(PROC ~ ENDP) : 시작 부분에 넣을 수 있는 정보, 프로시저가 수행하는 작업들에 대한 설명
  • .386 : 최소 CPU(386)을 표시
  • .model 메모리 모델을 명시(small, flat)하고 (매개 변수 전달에 사용되는 규약 지정
  • stdcall : MS-window 함수 호출
  • PROTO 프로시저에 대한 원형을 선언(ExitProcess PROTO / DumpRegs PROTO 등, 프로시저명 뒤에 삽입)
  • INVOKE :프로서지 또는 함수를 호출
  • COUNT : 등호 디렉티브(COUNT = 500), COUNT를 500이라는 값과 동일하게(대입)
  • EQU : 기호를 정수 수식이나 임의의 텍스트와 연관시킴(상수를 정의)
    • name EQU expression(수식, 문법에 맞는 표현)
    • name EQU symbol(= 또는 EQU로 이미 정의된 기호)
    • name EQU <...임의의 텍스트...>
    • matrix EQU 100 과 같은 형태
  • ALIGN : 다음 변수를 지정한 bound(1,2,4,16 중에 지정 가능)만큼 뒤에 주소에 저장(메모리 간의 간격 지정)
  • LABEL : 저장 공간을 할당하지 않으면서 레이블을 넣고 크기 속성을 주는 기능을 함(모든 표준 크기 속성이 사용될 수 있음)

 

 

피연산자(Operand)

  • 유형 : 상수, 상수 수식, 레지스터, 메모리 등
  • 어셈블리 언어 명령어는 0에서 3개의 피연산자를 가질 수 있음
    • 피연산자를 갖지 않는 명령어 : stc(set carry flag)
    • 한 개의 피연산자를 갖는 명령 : inc eax(add 1 to eax)
    • 두 개의 피연산자를 갖는 명령 : mov count, ebx(move ebx to count)
    • 세 개의 피연산자를 갖는 명령 : imul eax, ebx, 5(ebx에 5를 곱하여 eax에 저장)
  • 유형 : 즉시 값, 레지스터(CPU에 있는 명명된 레지스터를 사용), 메모리(메모리 위치)

피연산자 표기법

피연산자 설명
reg8 8비트 범용 레지스터 : AH, AL, BH, BL, CH, CL, DH, DL
reg16 16비트 범용 레지스터 : AX, BX, CX, DX, SI, DI, SP, BP
reg32 32비트 범용 레지스터 : EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP
reg 임의의 범용 레지스터
sreg 16비트 세그먼트 레지스터 : CS, DS, SS, ES, FS, GS
imm 8, 16 또는 32비트 즉시 값(숫자 수식을 사용)
imm8 8비트, 바이트 즉시 값
imm16 16비트, 워드 즉시 값
imm32 32비트, 더블 워드 즉시 값

 

주석 : 프로그램 작성자가 프로그램 소스 코드를 읽는 사람에게 프로그램 설계에 대한 정보를 제공

  • 한 줄 주석 : 세미 콜론(;) 뒤의 글자를 주석으로 처리
  • 블록 주석 : COMMENT 디렉티브와 사용자 정의 기호로 시작(예 : COMMENT ! ~ !)

 

주요 명령어

명령어 역할 활용 피연산자 비고
MOV 피연산자의 이동(대입) mov eax, 10000h 2개 피연산자 크기가 같아야 함
모두 메모리 피연산자일 수 없음
CS, EIP, IP를 목적지로 지정 불가
즉시 값이 세그먼트 레지스터로 이동될 수 없음
ADD 피연산자의 덧셈 add eax, 40000h 2개  
SUB 피연산자의 뺄셈 sub eax, 20000h 2개  
CALL 현재 값을 화면에 표시 call DumpRegs 1개  
EXIT 프로그램 종료 exit 0개  
END 어셈블된 프로그램의 마지막 줄 END main 1개  
DUP 상수 수식 반복 카운터
(여러 개의 저장 공간을 확보)
BYTE 4 DUP("STACK") "STACKSTACKSTACKSTACKSTACK"(20 Bytes)
MOVZX 소스를 목적지로 복사하고
값을 16/32비트로 제로 확장
movzx reg32, reg/mem8
movzx reg32, reg/mem16
movzx reg16, reg/mem8
2개 확장된 비트들은 0으로 채워짐
부호 없는 정수에서 사용
MOVSX 소스를 목적지로 복사하고
값을 16/32비트로 부호 확장
movsx reg32, reg/mem8
movsx reg32, reg/mem16movsx reg16, reg/mem8
2개 확장 비트들은 부호 비트(0,1)로 채워짐
부호 있는 정수에서 사용
XCHG 두 피연산자의 내용을 교환 XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
2개 즉시 값을 사용 가능
메모리끼리는 사용 불가
(레지스터를 임시저장소로 사용하는 형태로 우회 활용) 
INC 피연산자에 1을 더함 INC reg/mem 1개 플래그는 피연산자 값에 따라 변화
(캐리 플래그에는 영향을 주지 않음, 보조 캐리 플래그에는 영향)
DEC 피연산자에 1을 뺌 DEC reg/mem 1개
NEG 숫자를 2의 보수로 변환하고 부호를 바꿈 NEG reg./mem
1개  
OFFSET 이 연산자를 포함하는 세그먼트의 시작부터 변수 간의 거리를 반환 OFFSET Val 1개 OFFSET Val 자체가 하나의 값처럼 다른 연산자에 사용됨
-> mov esi, OFFSET Val
PTR 지정한 자료형만큼 뒤에서부터 식별하여 ax로 이동 mov ax, WORD PTR myDouble 1개 레지스터 출력에서는 WORD는 하위 4개 숫자(16진수이므로)를 반환함
변수에 1을 더할 수록 2자리 앞으로 이동하여 출력
TYPE 단일 원소 크기를 바이트 단위로 반환 mov eax, TYPE var 1 1개 BYTE = 1
WORD = 2
DWORD = 4
QWORD = 8
LENGTHOF 레이블과 같은 줄에 있는 값들로 정의되는 배열에 포함된 원소의 개수를 반환 byte1 BYTE 10, 20, 30
=> LENGTHOF array1 ; 3
베열 DUP가 중첩된 경우 중첩된 값의 곱을 반환
30 DUP(?) => 30
5 DUP(3 DUP(?)) => 15(5*3)
SIZEOF LENGTHOF와 TYPE의 곱을 반환 SIZEOF intArray 배열  
PUSH ESP를 감소시키고
피연산자를 스택에 복사
PUSH reg/mem16
PUSH reg.mem32
PUSH imm32
1개 *ESP : 스택 최상단 값의 주소를 가짐
16비트 피연산자는 ESP를 2 감소
32비트 피연산자는 ESP를 4 감소
POP ESP를 증가시키고
스택 원소를 목적지에 복사
POP reg/mem16
POP reg/mem32
1개 16비트 피연산자는 ESP를 2 증가
32비트 피연산자는 ESP를 4 증가
PUSHFD 32비트 EFLAGS 레지스터를 스택에 PUSH      
POPFD 스택을 32비트 EFLAGS 레지스터로 POP      
PUSHAD 모든 32비트 범용 레지스터를 순서대로 스택에 PUSH     *순서 : EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
POPAD PUSHAD 반대 순서로 스택에서 POP      
PUSHA 16비트 범용 레지스터를  순서대로 스택에 저장     *순서 : AX, CX, DX, BX, SP, BP, SI, DI
POPA PUSHA 반대 순서로 스택에서 POP      
CMP 정수(문자 코드 포함)를 비교(뺄셈) CMP destination, source   목적지 피연산자 값에 따라 OF, SF, ZF, CF, 보조 캐리 플래그, PF를 변화시킴
JZ/JNZ ZF가 CMP 명령어에 의해 1(JNZ=0)로 설정되면 지정 레이블로 점프 JZ L1
JNZ L1
조건부 점프J


JC/JNC CF가 1/0이면 점프  
JO/JNO OF가 1/0이면 점프  
JS/JNS SF가 1/0이면 점프  
JP/JNP PF가 1/0이면 점프  
JE/JNE 비교 결과가 같으면
비교 결과가 다르면
  동등 비교


JCXZ CX = 0이면  
JECXZ EXC = 0이면  
부호 없는 비교
JA : Jump if Above  = JNBE : Jump if Not Below or Equal (OP1 > OP2)
JAE : Jump if Above or Equal = JNB : Jump if Not Below (OP1 >= OP2)
JB : Jump if Below = JNAE : Jump if Not Above or Equal(OP1 < OP2)
JBE : Jump if Below or Equal = JNA Jump if Not Above(OP1 <= OP2)
부호 있는 비교
JG : Jump if Greater = JNLE : Jump if Not less than or equal(OP1 > OP2)
JGE : Jump if Greater than or Equal = JNL Jump if Not Less (OP1 >= OP2)
JL : Jump if Less = JNGE : Jump if Not Greater than or Equal(OP1 < OP2)
JLE : Jump if Less than or Equal = Jump if Not Greater(OP1 <= OP2) 
조건부 루프 명령어
LOOPZ(Loop if Zero) : ZF = 1인 경우 루프 수행(조건을 가진 LOOP 명령어)

LOOPE(Loop if Equal)
LOOPNZ(Loop if Not Zero) : ECX(부호 없음) 값이 0보다 크고 ZF=0인 동안 루프 수행
LOOPNE(Loop if Not Equal)
부울 명령어
연산 설명
AND 소스 피연산자와 목적지 피연산자 간의 부울 AND 연산
연산의 성질을 이용하여 마스킹(특정 비트를 0으로 만들어줌)
ASCII 대소문자의 변환에 활용(대문자와 소문자는 (뒤에서) 6번째 비트만 다름)
OR 소스 피연산자와 목적지 피연산자 간의 부울 OR 연산
XOR 소스 피연산자와 목적지 피연산자 간의 부울 XOR 연산
항상 OF / CF를 0으로 만듦
목적지 피연산자에 할당된 값에 의해 SF, ZF, PF,를 수정
NOT 목적지 피연산자에 대한 부울 NOT 연산
여집합 연산에 사용
TEST 소스 피연산자와 목적지 피연산자 간의 부울 AND 연산을 묵시적으로 수행하여 CPU 플래그를 적절하게 설정
BT, BTC, BTR, BTS 소스 피연산자의 비트 n을 캐리 플래그로 복사하고 목적지 피연산자의 같은 비트를 보수화/리셋(0)/셋(1) 시킴

 

 

주요 프로시저

프로시저명 역할 활용
DumpRegs 레지스터 내용을 출력 call DumpRegs
ExitProcess 현재의 프로그램 종료  
Clrscr 콘솔 원도우를 깨끗하게 지움  
Crlf 콘솔 윈도우의 커서를 다음 줄의 처음으로 이동  
Delay 지정된 msec 단위시간 동안 프로그램을 멈추게 함 eax에 시간 간격을 지정
DumpMem 일정 범위 위치의 메모리를 콘솔 윈도우에 16진수로 출력 메모리의 시작 주소를 ESI에
출력 단위의 크기를 EBX(바이트 단위)
출력 단위의 개수를 ECX에 전달
GetMseconds EAX 레지스터에 자정 이후 경과시간을 msec 단위로 반환 입력 변수 X
IsDigit AL에 있는 값이 유효한 10진수 자릿수에 대한 ASCII 코드인지 AL이 유효한 10진수 자릿수이면 ZF=1 / 그렇지 않으면 0
MsgBox 그래픽 팝업 메시지 상자를 표시 나타낼 문자열을 EDX에 저장
제목을 EBX에 저장(선택 사항)
MsgBoxAsk Yes/No 버튼을 갖는 그래픽 팝업 메시지 상자 표시 EAX에 사용자가 어떤 버튼을 선택했는지 저장
Parseinteger32 부호 있는 10진수 문자열(ASCII)을 32비트 이진수로 변환,
숫자가 아닌 문자 앞의 모둔 유효한 자리 숫자가 변환되며, 빈칸은 무시
EAX : 2진수 반환
ECX : 문자열 길이
EDX : 문자열 주소(OFFSET)
Random32 32비트 난수 정수를 생성하고 EAX에 반환 Seed를 사용
Randomize Random32, RandomRange의 시드 값을 초기화 시드는 100분의 1초 단위 시간 값
RandomRange 0 ~ n-1까지의 난수 정수 생성 EAX 값을 n(범위 매개변수)로 사용하며,
난수 정수가 EAX에 반환
ReadChar 키보드의 한 문자 입력을 읽고 AL에 반환 콘솔에 출력되지는 않음
ReadDec 키보드에 32비트 부호 없는 10진수를 읽어서 EAX에 빈환 빈 칸은 무시
정수가 비어있으면 EAX = 0 / CF = 1
정수가 빈 칸 만 가지면 EAX = 0 / CF = 1
정수가 2^13-1보다 크면 EAX = 0 / CF = 1
이외의 경우 변환된 정수를 저장(EAX)하고 CF = 1  
ReadHex 키보드에서 32비트 16진수 정수를 읽어서 대응되는 2진수 값을 EAX에 반환
잘못된 문자에 대한 어떠한 오류 검사도 수행하지 않음
A~F까지 자리 숫자(10~15) 입력 시 대소문자 모두 사용 가능
최대 8자리까지 입력 가능하며 앞의 빈칸이나 추가 문자는 무시
ReadInt 키보드에서 32비트 부호 있는 10진수를 읽어서 EAX에 반환 숫자가 아닌 문자를 만날때까지 모든 유효 자리로부터 계산(111AAA -> 111만 인식)
ReadKey 키보드 입력 버퍼를 조사(사용자의 입력 여부 식별) AL이 0이면 특수키(기능키, 화살표), ASCII 코드인 경우 특정 값을 의미
키 입력이 발견되면 ZF = 0
ReadString 키보드에서 문자열을 읽고 Enter키를 누를 때 멈춤 EDX : OFFSET
ECX :  사용자가 입력할 수 있는 최대 문자 수에 1을 더한 값으로 설정
(Null Byte에 대한 공간을 확보하기 위해)
EAX : 사용자가 입력한 문자수
SetTextColort 텍스트 출력에 대한 글자색과 배경색 설정 글자색 + (배경색 * 16)
색상표의 코드에 대응
SetLength Null 종료 문자열의 길이를 반환 EAX : 문자열 길이
EDX : OFFSET
WaitMsg "Press any key to continue..."를 표시하고 사용자의 입력을 대기  
WriteBin 정수를 ASCII 2진수 형식으로 콘솔 윈도우에 출력 EAX에 값 전달
2진수 비트는 4개씩 묶어서 표시
WriteBinB 32비트 정수를 ASCII 2진수 형식으로 콘솔 윈도우에 출력 EAX : 값 전달
EBX : 표시할 크기를 바이트 단위로 반환
WriteChar 콘솔 윈도우에 하나의 문자를 출력 문자/문자의 ASCII 코드를 AL에 전달
WriteDec 32비트 부호 없는 정수 앞에 0이 없는 10진수 형식으로 콘솔 윈도우에 출력 EAX :정수 전달
WriteHex 32비트 부호 없는 정수를 8자리 16진수 형식으로 콘솔 윈도우에 출력하며 필요한 경우 앞에 0들이 삽입 EAX :정수 전달
WriteHexB 32비트 부호 없는 정수를 16비트 형식으로 콘솔 윈도우에 출력하며 필요한 경우 앞에 0이 삽입 EAX : 값 전달
EBX : 표시할 크기를 바이트 단위로 반환
WriteInt 32비트 부호 있는 정수에 대해 부호는 포함하고 앞에 0이 없는 10진수 형식으로 콘솔 윈도우에 출력 EAX :정수 전달
WriteString 널(Null) 종료 문자열을 콘솔 윈도우에 출력 EDX : OFFSET
     

 

 

기본 출력

EAX = 00030000 EBX = 7EPDE000 ECX = 00000000 EDX = 003A1005
EST = 00000000 EDI = 00000000 EBP = 0056FD30 ESP = 0056FD30
EIP = 003A1024 EFL = 00000206 CF = 0   SF = 0   ZF = 0   OF = 0   AF = 0   PF = 1
출력의 처음 두 행은 32bit 범용 레지스터의 16진수 값(00030000 = 00030000h)
셋째 행은 Flags와 EIP(Extended Instruction Pointer), EFL(Extended Flags)

 

주요 고유 자료형

자료형 유형 크기
BYTE 부호 없는 정수 8Bit
SBYTE 부호 있는 정수 8Bit
WORD 부호 없는 정수 16Bit
SWORD 부호 있는 정수 16Bit
DWORD 부호 없는 정수 32Bit
SDWORD 부호 있는 정수 32Bit
FWORD 정수 48Bit
QWORD 정수 4Bit
TBYTE 정수 80Bit(10Byte)
REAL4 IEEE 짧은 실수 32Bit(4Byte)
REAL8 IEEE 긴 실수 64Bit(8Byte)
REAL10 IEEE 확장 실수 80Bit(10Byte)
구형 데이터 디렉티브
DB 8Bit 정수
DW 16Bit 정수
DD 32Bit 정수 또는 실수
DQ 64Bit 정수 또는 실수
DT 80bit(10Byte) 정수

 

 

플래그 유형

플래그명 용도 의미
캐리 플래그(Carry Flag, CF) 부호 없는 정수 연산의 오버플로우(제한 비트보다 더 큰 결과값) 표현 1인 경우 오버플로우 발생
패리티 플래그(Parity Flag, PF) 목적지 피연산자의 최하위 바이트 값의 홀수/짝수 1 = 짝수 / 0 = 홀수
사인 플래그(Sign Flag, SF) 연산 결과가 음수인지 식별 1= 음수 / 0 = 양수
오버플로우 플래그
(Overflow Flag, OF)
부호 있는 산술 연산의 오버플로우 식별 1인 경우 오버플로우 발생
제로 플래그(Zero Flag, ZF) 연산 결과가 0임을 나타냄 연산 결과가 0이면 1(결과와 반대)

 

데이터 정의문

  • 메모리에 변수를 위한 저장공간을 확보, 변수 이름의 입력은 선택적(필수 X)
  • 예 : count DWORD 12345 
  • 초기값을 반드시 설정해야 함(값에 ?를 입력하면 값을 초기화하지 않고 그대로 사용)
  • list BYTE 10, 20, 30, 40 등 배열 형태로 여러 개의 초기값 설정 가능(주소가 1Byte 단위로 증가)

 

정수의 제로/부호 확장

  • 작은 피연산자에서 큰 피연산자로 데이터를 복사하기 위해 MOV 데이터를 사용할 수 없음(두 연산자의 크기가 다르기 때문에)
  • 부호가 없는 경우 제로 확장을 사용하고 부호가 있는 경우 부호 확장을 사용함

 

직접 오프셋 피연산자

  • 변수의 이름에 변위를(위치 간격)을 더하여 직접 오프셋 피연산자를 만들 수 있음
  • 예 : mov al, [arrayB +1]
  • 배열인 경우 위치를 기반으로 연산(+1 : 배열의 두 번째 값)

간접 주소 지정

  • 32비트 범용 레지스터(EAX, EBX, ECX, EDX, ESI, EDI, ESP)의 특정 레지스터를 [eax]의 형태로 사용하여 해당 레지스터의 값을 표현
  • 레지스터를 간접 피연산자로 사용하기 전에 항상 초기화해야 함
  • 특정 레지스터에 배열을 입력한 뒤, inc reg 형태로 값을 증가시키고 [reg]를 호출하면 배열의 특정 위치에 순차적으로 접근 가능
  • add 정수 형태로 위치를 건너뛰어 접근할 수도 있음
반응형