mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 20:35:25 +00:00
437 lines
19 KiB
NASM
437 lines
19 KiB
NASM
|
.model large
|
|||
|
|
|||
|
;EXECSWAP.ASM
|
|||
|
; Swap memory and exec another program
|
|||
|
; Copyright (c) 1988 TurboPower Software
|
|||
|
; May be used freely as long as due credit is given
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;DATA SEGMENT BYTE PUBLIC
|
|||
|
.data
|
|||
|
EXTRN _BytesSwapped:DWORD ;Bytes to swap to EMS/disk
|
|||
|
EXTRN _EmsAllocated:BYTE ;True when EMS allocated for swap
|
|||
|
EXTRN _FileAllocated:BYTE ;True when file allocated for swap
|
|||
|
EXTRN _EmsHandle:WORD ;Handle of EMS allocation block
|
|||
|
EXTRN _FrameSeg:WORD ;Segment of EMS page frame
|
|||
|
EXTRN _FileHandle:WORD ;Handle of DOS swap file
|
|||
|
EXTRN _SwapName:BYTE ;ASCIIZ name of swap file
|
|||
|
EXTRN _PrefixSeg:WORD ;Base segment of program
|
|||
|
;DATA ENDS
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;CODE SEGMENT BYTE PUBLIC
|
|||
|
.code
|
|||
|
; ASSUME CS:CODE,DS:DATA
|
|||
|
PUBLIC EXECWITHSWAP, _FIRSTTOSAVE
|
|||
|
PUBLIC ALLOCATESWAPFILE, DEALLOCATESWAPFILE
|
|||
|
PUBLIC EMSINSTALLED, EMSPAGEFRAME
|
|||
|
PUBLIC ALLOCATEEMSPAGES, DEALLOCATEEMSHANDLE
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
FileAttr EQU 0 ;Swap file attribute (hidden+system)
|
|||
|
EmsPageSize EQU 16384 ;Size of EMS page
|
|||
|
FileBlockSize EQU 32768 ;Size of a file block
|
|||
|
StkSize EQU 128 ;Bytes in temporary stack
|
|||
|
lo EQU (WORD PTR 0) ;Convenient typecasts
|
|||
|
hi EQU (WORD PTR 2)
|
|||
|
ofst EQU (WORD PTR 0)
|
|||
|
segm EQU (WORD PTR 2)
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;Variables in CS
|
|||
|
EmsDevice DB 'EMMXXXX0',0 ;Name of EMS device driver
|
|||
|
UsedEms DB 0 ;1 if swapping to EMS, 0 if to file
|
|||
|
BytesSwappedCS DD 0 ;Bytes to move during a swap
|
|||
|
EmsHandleCS DW 0 ;EMS handle
|
|||
|
FrameSegCS DW 0 ;Segment of EMS page window
|
|||
|
FileHandleCS DW 0 ;DOS file handle
|
|||
|
PrefixSegCS DW 0 ;Segment of base of program
|
|||
|
Status DW 0 ;ExecSwap status code
|
|||
|
LeftToSwap DD 0 ;Bytes left to move
|
|||
|
SaveSP DW 0 ;Original stack pointer
|
|||
|
SaveSS DW 0 ;Original stack segment
|
|||
|
PathPtr DD 0 ;Pointer to program to execute
|
|||
|
CmdPtr DD 0 ;Pointer to command line to execute
|
|||
|
ParasWeHave DW 0 ;Paragraphs allocated to process
|
|||
|
CmdLine DB 128 DUP(0) ;Terminated command line passed to DOS
|
|||
|
Path DB 64 DUP(0) ;Terminated path name passed to DOS
|
|||
|
FileBlock1 DB 16 DUP(0) ;FCB passed to DOS
|
|||
|
FileBlock2 DB 16 DUP(0) ;FCB passed to DOS
|
|||
|
BooBoo DB '$'
|
|||
|
ComeBack DB '$'
|
|||
|
EnvironSeg DW 0 ;Segment of environment for child
|
|||
|
CmdLinePtr DD 0 ;Pointer to terminated command line
|
|||
|
FilePtr1 DD 0 ;Pointer to FCB file
|
|||
|
FilePtr2 DD 0 ;Pointer to FCB file
|
|||
|
TempStack DB StkSize DUP(0) ;Temporary stack
|
|||
|
StackTop LABEL WORD ;Initial top of stack
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;Macros
|
|||
|
MovSeg MACRO Dest,Src ;Set one segment register to another
|
|||
|
PUSH Src
|
|||
|
POP Dest
|
|||
|
ENDM
|
|||
|
|
|||
|
MovMem MACRO Dest,Src ;Move from memory to memory via AX
|
|||
|
MOV AX,Src
|
|||
|
MOV Dest,AX
|
|||
|
ENDM
|
|||
|
|
|||
|
InitSwapCount MACRO ;Initialize counter for bytes to swap
|
|||
|
MovMem LeftToSwap.lo,BytesSwappedCS.lo
|
|||
|
MovMem LeftToSwap.hi,BytesSwappedCS.hi
|
|||
|
ENDM
|
|||
|
|
|||
|
SetSwapCount MACRO BlkSize ;Return CX = bytes to move this block
|
|||
|
LOCAL FullBlk ;...and reduce total bytes left to move
|
|||
|
MOV CX,BlkSize ;Assume we'll write a full block
|
|||
|
CMP LeftToSwap.hi,0 ;Is high word still non-zero?
|
|||
|
JNZ FullBlk ;Jump if so
|
|||
|
CMP LeftToSwap.lo,BlkSize ;Low word still a block or more?
|
|||
|
JAE FullBlk ;Jump if so
|
|||
|
MOV CX,LeftToSwap.lo ;Otherwise, move what's left
|
|||
|
FullBlk:SUB LeftToSwap.lo,CX ;Reduce number left to move
|
|||
|
SBB LeftToSwap.hi,0
|
|||
|
ENDM
|
|||
|
|
|||
|
NextBlock MACRO SegReg, BlkSize ;Point SegReg to next block to move
|
|||
|
MOV AX,SegReg
|
|||
|
ADD AX,BlkSize/16 ;Add paragraphs to next segment
|
|||
|
MOV SegReg,AX ;Next block to move
|
|||
|
MOV AX,LeftToSwap.lo
|
|||
|
OR AX,LeftToSwap.hi ;Bytes left to move?
|
|||
|
ENDM
|
|||
|
|
|||
|
EmsCall MACRO FuncAH ;Call EMM and prepare to check result
|
|||
|
MOV AH,FuncAH ;Set up function
|
|||
|
INT 67h
|
|||
|
OR AH,AH ;Error code in AH
|
|||
|
ENDM
|
|||
|
|
|||
|
DosCallAH MACRO FuncAH ;Call DOS subfunction AH
|
|||
|
MOV AH,FuncAH
|
|||
|
INT 21h
|
|||
|
ENDM
|
|||
|
|
|||
|
DosCallAX MACRO FuncAX ;Call DOS subfunction AX
|
|||
|
MOV AX,FuncAX
|
|||
|
INT 21h
|
|||
|
ENDM
|
|||
|
|
|||
|
InitSwapFile MACRO
|
|||
|
MOV BX,FileHandleCS ;BX = handle of swap file
|
|||
|
XOR CX,CX
|
|||
|
XOR DX,DX ;Start of file
|
|||
|
DosCallAX 4200h ;DOS file seek
|
|||
|
ENDM
|
|||
|
|
|||
|
HaltWithError MACRO Level ;Halt if non-recoverable error occurs
|
|||
|
PUSH CS
|
|||
|
POP DS
|
|||
|
MOV DX,OFFSET BooBoo
|
|||
|
MOV AH,9
|
|||
|
INT 21h
|
|||
|
MOV AL,Level ;Set errorlevel
|
|||
|
DosCallAH 4Ch
|
|||
|
ENDM
|
|||
|
|
|||
|
MoveFast MACRO ;Move CX bytes from DS:SI to ES:DI
|
|||
|
CLD ;Forward
|
|||
|
RCR CX,1 ;Convert to words
|
|||
|
REP MOVSW ;Move the words
|
|||
|
RCL CX,1 ;Get the odd byte, if any
|
|||
|
REP MOVSB ;Move it
|
|||
|
ENDM
|
|||
|
|
|||
|
SetTempStack MACRO ;Switch to temporary stack
|
|||
|
MOV AX,OFFSET StackTop ;Point to top of stack
|
|||
|
MOV BX,CS ;Temporary stack in this code segment
|
|||
|
CLI ;Interrupts off
|
|||
|
MOV SS,BX ;Change stack
|
|||
|
MOV SP,AX
|
|||
|
STI ;Interrupts on
|
|||
|
ENDM
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;function ExecWithSwap(Path, CmdLine : string) : Word;
|
|||
|
EXECWITHSWAP PROC FAR
|
|||
|
PUSH BP
|
|||
|
MOV BP,SP ;Set up stack frame
|
|||
|
|
|||
|
;Move variables to CS where we can easily access them later
|
|||
|
MOV Status,1 ;Assume failure
|
|||
|
LES DI,[BP+6] ;ES:DI -> CmdLine
|
|||
|
MOV CmdPtr.ofst,DI
|
|||
|
MOV CmdPtr.segm,ES ;CmdPtr -> command line string
|
|||
|
LES DI,[BP+10] ;ES:DI -> Path
|
|||
|
MOV PathPtr.ofst,DI
|
|||
|
MOV PathPtr.segm,ES ;PathPtr -> path to execute
|
|||
|
MOV SaveSP,SP ;Save stack position
|
|||
|
MOV SaveSS,SS
|
|||
|
MovMem BytesSwappedCS.lo,_BytesSwapped.lo
|
|||
|
MovMem BytesSwappedCS.hi,_BytesSwapped.hi
|
|||
|
MovMem EmsHandleCS,_EmsHandle
|
|||
|
MovMem FrameSegCS,_FrameSeg
|
|||
|
MovMem FileHandleCS,_FileHandle
|
|||
|
MovMem PrefixSegCS,_PrefixSeg
|
|||
|
InitSwapCount ;Initialize bytes LeftToSwap
|
|||
|
|
|||
|
;Check for swapping to EMS or file
|
|||
|
CMP _EmsAllocated,0 ;Check flag for EMS method
|
|||
|
JZ NotEms ;Jump if EMS not used
|
|||
|
JMP WriteE ;Swap to EMS
|
|||
|
NotEms: CMP _FileAllocated,0 ;Check flag for swap file method
|
|||
|
JNZ WriteF ;Swap to file
|
|||
|
JMP ESDone ;Exit if no swapping method set
|
|||
|
|
|||
|
;Write to swap file
|
|||
|
WriteF: MovSeg DS,CS ;DS = CS
|
|||
|
InitSwapFile ;Seek to start of swap file
|
|||
|
JNC EF0 ;Jump if success
|
|||
|
JMP ESDone ;Exit if error
|
|||
|
EF0: SetSwapCount FileBlockSize ;CX = bytes to write
|
|||
|
MOV DX,OFFSET _FIRSTTOSAVE ;DS:DX -> start of region to save
|
|||
|
DosCallAH 40h ;File write
|
|||
|
JC EF1 ;Jump if write error
|
|||
|
CMP AX,CX ;All bytes written?
|
|||
|
JZ EF2 ;Jump if so
|
|||
|
EF1: JMP ESDone ;Exit if error
|
|||
|
EF2: NextBlock DS,FileBlockSize ;Point DS to next block to write
|
|||
|
JNZ EF0 ;Loop if bytes left to write
|
|||
|
MOV UsedEms,0 ;Flag we used swap file for swapping
|
|||
|
JMP SwapDone ;Done swapping out
|
|||
|
|
|||
|
;Write to EMS
|
|||
|
WriteE: MOV ES,_FrameSeg ;ES -> page window
|
|||
|
MOV DX,_EmsHandle ;DX = handle of our EMS block
|
|||
|
XOR BX,BX ;BX = initial logical page
|
|||
|
MovSeg DS,CS ;DS = CS
|
|||
|
EE0: XOR AL,AL ;Physical page 0
|
|||
|
EmsCall 44h ;Map physical page
|
|||
|
JZ EE1 ;Jump if success
|
|||
|
JMP ESDone ;Exit if error
|
|||
|
EE1: SetSwapCount EmsPageSize ;CX = Bytes to move
|
|||
|
XOR DI,DI ;ES:DI -> base of EMS page
|
|||
|
MOV SI,OFFSET _FIRSTTOSAVE ;DS:SI -> region to save
|
|||
|
MoveFast ;Move CX bytes from DS:SI to ES:DI
|
|||
|
INC BX ;Next logical page
|
|||
|
NextBlock DS,EmsPageSize ;Point DS to next page to move
|
|||
|
JNZ EE0 ;Loop if bytes left to move
|
|||
|
MOV UsedEms,1 ;Flag we used EMS for swapping
|
|||
|
|
|||
|
;Shrink memory allocated to this process
|
|||
|
SwapDone:MOV AX,PrefixSegCS
|
|||
|
MOV ES,AX ;ES = segment of our memory block
|
|||
|
DEC AX
|
|||
|
MOV DS,AX ;DS = segment of memory control block
|
|||
|
MOV CX,DS:[0003h] ;CX = current paragraphs owned
|
|||
|
MOV ParasWeHave,CX ;Save current paragraphs owned
|
|||
|
SetTempStack ;Switch to temporary stack
|
|||
|
MOV AX,OFFSET _FIRSTTOSAVE+15
|
|||
|
MOV CL,4
|
|||
|
SHR AX,CL ;Convert offset to paragraphs
|
|||
|
ADD BX,AX
|
|||
|
SUB BX,PrefixSegCS ;BX = new paragraphs to keep
|
|||
|
DosCallAH 4Ah ;SetBlock
|
|||
|
JNC EX0 ;Jump if successful
|
|||
|
JMP EX5 ;Swap back and exit
|
|||
|
|
|||
|
;Set up parameters and call DOS Exec
|
|||
|
EX0: MOV AX,ES:[002Ch] ;Get environment segment
|
|||
|
MOV EnvironSeg,AX
|
|||
|
MovSeg ES,CS ;ES = CS
|
|||
|
LDS SI,PathPtr ;DS:SI -> path to execute
|
|||
|
MOV DI,OFFSET Path ;ES:DI -> local ASCIIZ copy
|
|||
|
CLD
|
|||
|
; LODSB ;Read current length
|
|||
|
; CMP AL,63 ;Truncate if exceeds space set aside
|
|||
|
; JB EX1
|
|||
|
; MOV AL,63
|
|||
|
;EX1: MOV CL,AL
|
|||
|
; XOR CH,CH ;CX = bytes to copy
|
|||
|
MOV CX, 63
|
|||
|
REP MOVSB
|
|||
|
; XOR AL,AL
|
|||
|
; STOSB ;ASCIIZ terminate
|
|||
|
LDS SI,CmdPtr ;DS:SI -> Command line to pass
|
|||
|
MOV DI,OFFSET CmdLine ;ES:DI -> Local terminated copy
|
|||
|
; LODSB
|
|||
|
; CMP AL,126 ;Truncate command if exceeds space
|
|||
|
; JB EX2
|
|||
|
; MOV AL,126
|
|||
|
;EX2: STOSB
|
|||
|
; MOV CL,AL
|
|||
|
; XOR CH,CH ;CX = bytes to copy
|
|||
|
MOV CX, 127
|
|||
|
REP MOVSB
|
|||
|
; MOV AL,0DH ;Terminate with ^M
|
|||
|
; STOSB
|
|||
|
|
|||
|
MovSeg DS,CS ;DS = CS
|
|||
|
MOV SI,OFFSET CmdLine
|
|||
|
MOV CmdLinePtr.ofst,SI
|
|||
|
MOV CmdLinePtr.segm,DS ;Store pointer to command line
|
|||
|
; INC SI
|
|||
|
MOV DI,OFFSET FileBlock1
|
|||
|
MOV FilePtr1.ofst,DI
|
|||
|
MOV FilePtr1.segm,ES ;Store pointer to filename 1, if any
|
|||
|
DosCallAX 2901h ;Parse FCB
|
|||
|
MOV DI,OFFSET FileBlock2
|
|||
|
MOV FilePtr2.ofst,DI
|
|||
|
MOV FilePtr2.segm,ES ;Store pointer to filename 2, if any
|
|||
|
DosCallAX 2901h ;Parse FCB
|
|||
|
MOV DX,OFFSET Path
|
|||
|
MOV BX,OFFSET EnvironSeg
|
|||
|
DosCallAX 4B00h ;Exec
|
|||
|
JC EX3 ;Jump if error in DOS call
|
|||
|
XOR AX,AX ;Return zero for success
|
|||
|
EX3: MOV Status,AX ;Save DOS error code
|
|||
|
|
|||
|
;Set up temporary stack and reallocate original memory block
|
|||
|
SetTempStack ;Set up temporary stack
|
|||
|
MOV ES,PrefixSegCS
|
|||
|
MOV BX,ParasWeHave
|
|||
|
DosCallAH 4Ah ;SetBlock
|
|||
|
JNC EX4 ;Jump if no error
|
|||
|
HaltWithError 0FFh ;Must halt if failure here
|
|||
|
EX4: InitSwapCount ;Initialize LeftToSwap
|
|||
|
|
|||
|
;Check which swap method is in use
|
|||
|
EX5: PUSH CS
|
|||
|
POP DS
|
|||
|
MOV DX,OFFSET ComeBack
|
|||
|
MOV AH,9
|
|||
|
INT 21h
|
|||
|
CMP UsedEms,0
|
|||
|
JZ ReadF ;Jump to read back from file
|
|||
|
JMP ReadE ;Read back from EMS
|
|||
|
|
|||
|
;Read back from swap file
|
|||
|
ReadF: MovSeg DS,CS ;DS = CS
|
|||
|
InitSwapFile ;Seek to start of swap file
|
|||
|
JNC EF3 ;Jump if we succeeded
|
|||
|
HaltWithError 0FEh ;Must halt if failure here
|
|||
|
EF3: SetSwapCount FileBlockSize ;CX = bytes to read
|
|||
|
MOV DX,OFFSET _FIRSTTOSAVE ;DS:DX -> start of region to restore
|
|||
|
DosCallAH 3Fh ;Read file
|
|||
|
JNC EF4 ;Jump if no error
|
|||
|
HaltWithError 0FEh ;Must halt if failure here
|
|||
|
EF4: CMP AX,CX
|
|||
|
JZ EF5 ;Jump if full block read
|
|||
|
HaltWithError 0FEh ;Must halt if failure here
|
|||
|
EF5: NextBlock DS,FileBlockSize ;Point DS to next page to read
|
|||
|
JNZ EF3 ;Jump if bytes left to read
|
|||
|
JMP ESDone ;We're done
|
|||
|
|
|||
|
;Copy back from EMS
|
|||
|
ReadE: MOV DS,FrameSegCS ;DS -> page window
|
|||
|
MOV DX,EmsHandleCS ;DX = handle of our EMS block
|
|||
|
XOR BX,BX ;BX = initial logical page
|
|||
|
MovSeg ES,CS ;ES = CS
|
|||
|
EE3: XOR AL,AL ;Physical page 0
|
|||
|
EmsCall 44h ;Map physical page
|
|||
|
JZ EE4 ;Jump if success
|
|||
|
HaltWithError 0FDh ;Must halt if failure here
|
|||
|
EE4: SetSwapCount EmsPageSize ;CX = Bytes to move
|
|||
|
XOR SI,SI ;DS:SI -> base of EMS page
|
|||
|
MOV DI,OFFSET _FIRSTTOSAVE ;ES:DI -> region to restore
|
|||
|
MoveFast ;Move CX bytes from DS:SI to ES:DI
|
|||
|
INC BX ;Next logical page
|
|||
|
NextBlock ES,EmsPageSize ;Point ES to next page to move
|
|||
|
JNZ EE3 ;Jump if so
|
|||
|
|
|||
|
ESDone: CLI ;Switch back to original stack
|
|||
|
MOV SS,SaveSS
|
|||
|
MOV SP,SaveSP
|
|||
|
STI
|
|||
|
MOV AX,SEG DGROUP
|
|||
|
MOV DS,AX ;Restore DS
|
|||
|
MOV AX,Status ;Return status
|
|||
|
POP BP
|
|||
|
RET 8 ;Remove parameters and return
|
|||
|
EXECWITHSWAP ENDP
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;Label marks first location to swap
|
|||
|
_FIRSTTOSAVE:
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;function AllocateSwapFile : Boolean;
|
|||
|
ALLOCATESWAPFILE PROC FAR
|
|||
|
MOV CX,FileAttr ;Attribute for swap file
|
|||
|
MOV DX,OFFSET _SwapName ;DS:DX -> ASCIIZ swap name
|
|||
|
DosCallAH 3Ch ;Create file
|
|||
|
MOV _FileHandle,AX ;Save handle assuming success
|
|||
|
MOV AL,0 ;Assume failure
|
|||
|
JC ASDone ;Failed if carry set
|
|||
|
INC AL ;Return true for success
|
|||
|
ASDone: RET
|
|||
|
ALLOCATESWAPFILE ENDP
|
|||
|
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;procedure DeallocateSwapFile;
|
|||
|
DEALLOCATESWAPFILE PROC FAR
|
|||
|
MOV BX,_FileHandle ;Handle of swap file
|
|||
|
DosCallAH 3Eh ;Close file
|
|||
|
XOR CX,CX ;Normal attribute
|
|||
|
MOV DX,OFFSET _SwapName ;DS:DX -> ASCIIZ swap name
|
|||
|
DosCallAX 4301h ;Set file attribute
|
|||
|
DosCallAH 41h ;Delete file
|
|||
|
RET
|
|||
|
DEALLOCATESWAPFILE ENDP
|
|||
|
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;function EmsInstalled : Boolean;
|
|||
|
EMSINSTALLED PROC FAR
|
|||
|
|
|||
|
PUSH DS
|
|||
|
MovSeg DS,CS ;DS = CS
|
|||
|
MOV DX,OFFSET EmsDevice ;DS:DX -> EMS driver name
|
|||
|
DosCallAX 3D02h ;Open for read/write
|
|||
|
POP DS
|
|||
|
MOV BX,AX ;Save handle in case one returned
|
|||
|
MOV AL,0 ;Assume FALSE
|
|||
|
JC EIDone
|
|||
|
DosCallAH 3Eh ;Close file
|
|||
|
MOV AL,1 ;Return TRUE
|
|||
|
|
|||
|
EIDone: RET
|
|||
|
|
|||
|
EMSINSTALLED ENDP
|
|||
|
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;function EmsPageFrame : Word;
|
|||
|
EMSPAGEFRAME PROC FAR
|
|||
|
|
|||
|
EmsCall 41h ;Get page frame
|
|||
|
MOV AX,BX ;AX = segment
|
|||
|
JZ EPDone ;Done if Error = 0
|
|||
|
XOR AX,AX ;Else segment = 0
|
|||
|
|
|||
|
EPDone: RET
|
|||
|
|
|||
|
EMSPAGEFRAME ENDP
|
|||
|
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;function AllocateEmsPages(NumPages : Word) : Word;
|
|||
|
ALLOCATEEMSPAGES PROC FAR
|
|||
|
|
|||
|
MOV BX,SP ;Set up stack frame
|
|||
|
MOV BX,SS:[BX+4] ;BX = NumPages
|
|||
|
EmsCall 43h ;Allocate EMS
|
|||
|
MOV AX,DX ;Assume success
|
|||
|
JZ APDone ;Done if not 0
|
|||
|
MOV AX,0FFFFh ;$FFFF for failure
|
|||
|
|
|||
|
APDone: RET 2 ;Remove parameter and return
|
|||
|
|
|||
|
ALLOCATEEMSPAGES ENDP
|
|||
|
|
|||
|
;-----------------------------------------------------------------------------
|
|||
|
;procedure DeallocateEmsHandle(Handle : Word);
|
|||
|
DEALLOCATEEMSHANDLE PROC FAR
|
|||
|
|
|||
|
MOV BX,SP ;Set up stack frame
|
|||
|
MOV DX,SS:[BX+4] ;DX = Handle
|
|||
|
EmsCall 45h ;Deallocate EMS
|
|||
|
|
|||
|
RET 2 ;Remove parameter and return
|
|||
|
|
|||
|
DEALLOCATEEMSHANDLE ENDP
|
|||
|
|
|||
|
;CODE ENDS
|
|||
|
END
|
|||
|
|