어셈블리 명령어
INC(Increase)
피연산자에 1을 더한다.
연산 결과에 따라 ZF(Zero Flag)나 OF(Overflow Flag)가 세트될 수 있다.
INC reg
INC mem
위의 표기(reg, mem)는 피연산자가 레지스터나 메모리가 될 수 있다는 의미이다.
DEC(Decrease)
피연산자에 1을 빼는 명령이다.
션산 결과에 따라 ZF나 OF가 세트될 수 있다.
DEC reg
DEC mem
ADD(add)
Destination에 Source의 값을 더해서 Destination에 저장하는 명령이다.
연산 결과에 따라서 ZF, OF, CF가 세트될 수 있다.
ADD destination, Source
ADD reg, reg
ADD reg, imm
ADD mem, reg
ADD mem, imm
ADD reg, mem
ADD eax, 123
위 명령은 eax 레지스터에 123을 더해서 eax 레지스터에 저장한다.
SUB(Subtract)
Destination에 Source의 값을 빼서 Destination에 저장하는 명령이다.
연산결과에 따라서 ZF, OF, CF가 세트될 수 있다.
SUB destination, Source
SUB reg, reg
SUB reg, imm
SUB mem, reg
SUB mem, imm
SUB reg, mem
SUB eax, 123
위 명령은 eax 레지스터에 123을 빼해서 eax 레지스터에 저장한다.
MUL(Unsigned Integer Multiply)
부호 없는 al, ax, eax의 값을 피연산자와 곱한다. 피연산자가 8비트이면 al과 곱해서 ax에 저장되고 16비트이면 ax와 곱하고 dx:ax에 저장된다.
MUL reg
MUL mem
결과에 따라 OF, ZF 플래그가 세트될 수 있다.
IMUL(Integer Multiplication)
부호 있는 al, ax, eax의 값을 피연산자와 곱한다. 결과에 따라 CF, OF가 세트될 수 있다.
IMUL r/m8
IMUL r/m16
IMUL r/m32
단일 피 연산자이고 피연산자를 al, ax, eax에 곱한다.
IMUL destination, value
IMUL r16, r/m16 IMUL r16, imm8
IMUL r32, r/m32 IMUL r32, imm8
IMUL r16, imm16 IMUL r32, imm32
value를 al, ax, eax와 곱해서 destination에 저장한다.
IMUL destination, value, value
IMUL r16, r/m16, imm8 IMUL r16, r/m16, imm16
IMUL r32, r/m32, imm8 IMUL r32, r/m32, imm32
value끼리 곱해서 destination에 저장한다.
연산결과가 destination 레지스터의 크기보다 크다면 OF, CF가 세트된다.
DIV(Unsigned Integer Divide)
8, 16, 32비트 부호 없는 정수의 나눗셈을 수행한다.
DIV reg
DIV mem
결과에 따라서 CF, OF, ZF가 세트될 수 있다.
MOV(Move)
Source에서 Destintion으로 데이터를 복사한다.
MOV Destination, Source
MOV reg, reg
MOV reg, imm
MOV mem, reg
MOV mem, imm
MOV reg, mem
MOVS(Move String)
Source에서 Destination으로 데이터를 복사한다.
MOVS Destination, Source
MOVSB, MOVSW, MOVSD(Move String)
SI 또는 ESI 레지스터에 의해 지정된 메모리 주소의 내용을 DI 또는 EDI 레지스터에 의해 지정되는 메모리 주소로 복사한다.
MOVSB는 BYTE 단위로 복사, MOVSW는 WORD 단위로 복사, MOVSD는 DWORD 단위로 복사한다. 방향 플래그(DF)가 1로 세트되어 있으면 ESI와 EDI는 복사 시에 감소하게 되고 DF가 0으로 세트되어 있으면 ESI와 EDI는 복사 시에 증가하게 된다.
방향 플래그(DF)를 설정할 수 있는 명령어는 잠시 후에 살펴보자.
MOVSB
MOVSW
MOVSD
MOVSX(Move with Sign-Extend)
BYTE나 WORD 크기의 피연산자를 WORD나 DWORD 크기로 확장하고 부호는 그대로 유지한다.
MOVSX reg32, reg16 MOVSX reg32, mem16
MOVSX reg16, reg8 MOVSX reg16, mem8
MOVZX(Move with Zero-Extend)
BYTE나 WORD 크기의 피연산자를 WORD나 DWORD 크기로 확장하고 남은 비트는 0으로 채운다.
MOVZX reg32, reg16 MOVZX reg32, mem16
MOVZX reg16, reg8 MOVZX reg16, mem8
INT(interrupt)
소프트웨어 인터럽트를 발생시켜 운영체제의 서브루틴을 호출한다.
INT imm
INT 3
AND(Logical AND)
Destination과 Source 피연산자의 각 비트가 AND 연산된다.
AND 연산은 각 비트가 모두 1일 때만 결과 값이 1이 된다.
Destination : 10011100
Source : 11001010
위의 두 피연산자를 AND 연산하면 결과는 10001000이 된다.
AND 연산을 통해서 OF, CF가 0으로 세트되고 결과에 따라서 ZF가 1로 세트될 수 있다.
AND reg, reg AND reg, imm
AND mem, reg AND mem, imm
AND reg, mem
OR(Inclusive OR)
Destination과 Source 피연산자의 각 비트가 OR 연산된다.
OR 연산은 각 비트가 모두 0이면 결과는 0이고 모두 0이 아니면 결과는 1이 된다.
Destination : 10011100
Source : 11001010
위의 두 피연산자를 OR 연산하면 결과는 11011110이 된다.
OR 연산을 통해서 OF, CF가 0으로 세트되고 결과에 따라서 ZF가 1로 세트될 수 있다.
OR reg, reg OR reg, imm
OR mem, reg OR mem, imm
OR reg, mem
XOR(Exclusive OR)
Destination과 Source 피연산자의 각 비트가 XOR 연산된다.
XOR 연산은 각 비트가 서로 다른 값일 때만 결과가 1이다. 같은 값이라면 결과는 0이 된다.
Destination : 10011100
Source : 11001010
위의 두 피연산자를 XOR 연산하면 결과는 01010110이 된다.
XOR 연산을 통해서 OF, CF가 0으로 세트되고 결과에 따라서 ZF가 1로 세트될 수 있다.
피연산자의 두 값이 같은 값이라면 결과는 항상 0이 된다.
레지스터를 0으로 초기화시킬 때 MOV 명령어를 사용하기보다는 XOR reg, reg으로 많이 사용한다.
XOR reg, reg XOR reg, imm
XOR mem, reg XOR mem, imm
XOR reg, mem
※참고로 리버싱을 하다 보면 XOR EBX, EBX와 같이 XOR 연산자를 이용하여 EBX 같은 값들을 0으로 초기화시키는 경우가 있는데, 이는 자신과 같은 값을 XOR시키면 0이 되기 때문에 짧은 코드로 초기화가 가능하다.
TEST(Test)
두 피연산자 사이에 논리적인 AND 연산을 수행하여 플래그 레지스터에 영향을 주지만 결과값은 저장하지 않는다. OF, CF는 항상 0으로 세트되고 TEST 연산 결과값이 0이면 ZF가 1로 세트, 0이 아니면 ZF가 0으로 세트된다.
TEST reg, reg TEST reg, imm
TEST mem, reg TEST mem, imm
TEST reg, mem
STC(Set Carry Flag)
캐리 플래그(CF)를 1로 세트한다.
STC
CLC(Clear Carry Flag)
캐리 플래그(CF)를 0으로 세트한다.
CLC
STD(Set Direction Flag)
방향 플래그(DF)를 1로 세트한다.
STD
CLD(Clear Direction Flag)
방향 플래그(DF)를 0으로 세트한다.
CLD
STI(Set Interrupt Flag)
인터럽트 플래그(IF)를 1로 세트한다.
STI
CLI(Clear Interrupt Flag)
인터럽트 플래그(IF)를 0으로 세트한다
CLI
SHL(Shift Left)
Destination 피연산자를 Source 피연산자의 크기만큼 왼쪽으로 각 비트를 시프트시킨다.
최상위 비트는 캐리 플래그(CF)로 복사되고 최하위 비트는 0으로 채워진다.
SHL reg, imm8 SHL mem, imm8
SHL reg, CL SHL mem, CL
SHR(Shift Right)
Destination 피연산자를 Source 피연산자의 크기만큼 오른쪽으로 각 비트를 시프트시킨다.
최상위 비트는 0으로 채워지고 최하위 비트는 캐리 플래그(CF)로 복사된다.
SHR reg, imm8 SHR mem, imm8
SHR reg, CL SHR mem, CL
PUSH(Push on Stack)
스택에 값을 넣는다.
ESP의 값이 4만큼 줄어들고 이 위치에 새로운 값이 채워진다.
PUSH reg16 PUSH reg32
PUSH mem16 PUSH mem32
PUSH imm16 PUSH imm32
PUSHAD(Push All)
EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터의 값을 스택에 PUSH한다.
레지스터들의 값을 보관해야 할 필요가 있을 때 사용한다.
PUSHAD
PUSHFD(Push Flags)
플래그 레지스터를 스택에 PUSH한다.
플래그 레지스터의 값을 보관해야 할 필요가 있을 때 사용한다.
PUSHFD
POP(Pop from Stack)
ESP 레지스터가 가리키고 있는 위치의 스택 공간에서 4byte 만큼을 Destination 피연산자에 복사하고 ESP 레지스터의 값에 4를 더한다.
POP destination
POP reg16 POP reg32
POP mem16 POP mem32
POPAD(Pop All Flags from Stack)
스택에 존재하는 값을 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터로 POP한다.
PUSHAD 명령어로 스택에 보관해 놓은 레지스터 정보를 다시 이용하려고 할 때 사용한다.
POPAD
POPFD(Pop Flags from Stack)
스택에 존재하는 값을 플래그 레지스터로 POP한다.
PUSHFD 명령어로 스택에 보관해 놓은 레지스터 정보를 다시 이용하려고 할 때 사용한다.
POPFD
XCHG(Exchange)
두 피연산자의 내용이 서로 교환된다.
XCHG 명령은 imm 값이 피연산자로 올 수 없다.
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
NEG(Negate)
피연산자의 2의 보수를 계산하여 결과를 피연산자에 저장한다.
NEG reg
NEG mem
PTR
피연산자의 크기를 재설정한다.
MOV eax, DWORD PTR value
위의 명령어는 value의 크기를 DWORD의 크기로 재설정하여 eax 레지스터에 복사하라는 의미이다.
OFFSET
세그먼트의 시작으로부터 변수가 위치한 거리까지의 상대적 거리를 리턴한다.
MOV esi, OFFSET value
위 명령어는 value가 존재하는 위치를 세그먼트 시작 지점으로부터의 상대적 거리로 구해서 esi 레지스터에 복사하라는 의미이다.
LEA(Load Effective Address)
Source 피연산자의 유효 주소를 계산하여 Destination 피연산자에 복사한다.
간단히 주소를 알아내서 복사하는 명령어라고 생각하면 쉽다.
LEA reg, mem
REP(Repeat String)
ECX 레지스터를 카운터로 사용해서 문자열 관련 명령을 ECX > 0 인 동안 반복한다.
REP MOVS destination, source
JMP(Jump Unconditionally to Lable)
피연산자의 위치로 실행 흐름이 변경된다. 피연산자가 가리키는 코드로 점프 뛰어서 실행한다고 생각하면 된다. 피연산자에는 레이블이나 레지스터, 메모리 값이 올 수 있다.
short 점프는 -127~127 바이트 범위 안에서 사용되고 near 점프는 같은 세그먼트 내부에서 사용된다. far 점프는 현재 세그먼트를 벗어날 때 사용되며 JMP 명령어는 되돌아올 리턴 어드레스 값을 저장하지 않는다. 이 명령어는 무조건 점프 명렁어인데 점프 명령은 어떤 형식에 맞을 때만 점프하는 조건 형식의 점프 명령도 있다. 이 명령어들은 잠시 후에 살펴보자.
JMP shortlabel JMP reg16
JMP nearlabel JMP mem16
JMP farlabel JMP mem32
CALL(Call a Procedure)
함수 호출시 사용된다. JMP 명령어 같이 프로그램의 실행 흐름이 변경되지만 JMP 명령어와 다른 점은 되돌아올 리턴 어드레스(CALL 다음 명령)를 스택에 저장한다는 것이다. 되돌아올 주소를 저장하기 때문에 함수 호출 후 원래 위치로 실행 흐름을 되돌릴 수 있다. 호출한 함수가 일을 다 마치면 원래 위치에서 다시 프로그램이 실행될 수 있음을 의미한다.
CALL nearlable CALL farlabel
CALL mem16 CALL mem32 CALL reg
(디버깅을 하면서 보게 되는 Call 형식들)
Call 함수 주소
Call DWORD PTR[EAX+5]
Call <JMP to API> 특정 api 지목
CMP(Compare)
두 피연산자를 비교하는 작업을 한다. Destination 피연산자에서 Source 피연산자를 묵시적으로 빼서 값을 비교한다. 두 피연산자의 값이 같다면 결과는 0이 되고 제로 플래그(ZF)가 1로 세트된다. 다르다면 제로 플래그(ZF)는 0으로 세트된다.
CMP reg, reg CMP reg, imm
CMP mem, reg CMP mem, imm
CMP reg, mem
NOP(No Operation)
아무 일도 하지 않는 명령어이다. 리버싱 작업에서 목적에 따라 유용하게 사용될 수 있다.
NOP
조건 점프 명령
조건 점프 명령은 JMP 명령어와는 다르게 CMP 명령같이 특정 플래그 레지스터를 변경시킬 수 있는 명령어를 통해서 특정 조건이 만족하게 된다면 점프를 수행하게 되는 명령어이다. 명령어 자체가 처음 볼땐 어떤 일을 하는지 이해하기가 힘들게 되어 있다. 다음에 여러 조건 형식의 점프 명령과 명령의 의미, 그리고 어떤 조건(플래그 레지스터의 상태)을 만족해야 명령어가 수행되는지 표로 정리하였다.
명령어
명령어의 의미
명령어가 수행되기 위한 플래그
레지스터와 범용 레지스터의 상태
JA
Jump if (unsigned) above
CF=0 and ZF=0
JAE
Jump if (unsigned) above or equal
CF=0
JB
Jump if (unsigned) below
CF=1
JBE
Jump if (unsigned) below or eqaul
CF=1 or ZF=1
JC
Jump if carry flag set
CF=1
JCXZ
Jump if CX is 0
CX=0
JE
Jump if equal
ZF=1
JECXZ
Jump if ECX is 0
ECX=0
JG
Jump if (signed) greater
ZF=0 and SF=0
JGE
Jump if (signed) greater or equal
SF=OF
JL
Jump if (signed) less
SF!=OF
JLE
Jump if (signed) less or equal
ZF=1 and OF!=OF
JNA
Jump if (unsigned) not above
CF=1 or ZF=1
JNAE
Jump if (unsigned) not above or equal
CF=1
JNB
Jump if (unsigned) not below
CF=0
JNBE
Jump if (unsigned) not below or equal
CF=0 and ZF=0
JNC
Jump if carry flag not set
CF=0
JNE
Jump if not equal
ZF=0
JNG
Jump if (signed) not greater
ZF=1 or SF!=OF
JNGE
Jump if (signed) not greater or equal
SF!=OF
JNL
Jump if (signed) not less
SF=OF
JNLE
Jump if (signed) not less or equal
ZF=0 and SF=OF
JNO
Jump if oeverflow flag not set
OF=0
JNP
Jump if parity flag not set
PF=0
JNS
Jump if sign flag not set
SF=0
JNZ
Jump if not zero
ZF=0
JO
Jump if overflow flag is set
OF=1
JP
Jump if parity flag set
PF=1
JPE
Jump if parity is equal
PF=1
JPO
Jump if parity if odd
PF=0
JS
Jump if sign flag is set
SF=1
JZ
Jump is zero
ZF=1