Sappiamo che, per un programma TSR, la capacità di disinstallarsi (liberando la RAM allocata e rilasciando i vettori di interrupt agganciati) è una caratteristica utile.
Sappiamo anche, per esperienza, che non tutti i TSR ne sono dotati e pertanto possono essere rimossi dalla memoria esclusivamente con un nuovo bootstrap.
Ecco allora una utility di qualche aiuto: essa è, a sua volta, un TSR, in grado di disinstallare tutti i TSR caricati successivamente.
/********************
Barninga_Z! - 1991
ZAPTSR.C - TSR in grado di rimuovere dalla ram tutti i TSR
caricati dopo la propria installazione (nonche' se' medesimo),
ripristinando i vettori di interrupt attivi al momento del
proprio caricamento e liberando tutti i blocchi di memoria
allocati ad un PSP maggiore del proprio. E' possibile
installarne piu' copie in RAM; quando si richiede la
disinstallazione (invocare con un asterisco come parametro
sulla command line) viene disinstallata l'ultima copia
installata (criterio L.I.F.O.) e, con essa, tutti i TSR
caricati successivamente. Passando un piu' (+) sulla command
line, ZAPTSR si installa allocando a se' stesso tutti i
blocchi di RAM liberi ad indirizzi minori del proprio.
STRATEGIA: il numero di copia installata, l'indirizzo di
segmento del MCB della porzione residente e la tavola
dei vettori sono conservati nello spazio riservato dalla
funzione dummy GData(). La comunicazione tra porzione
residente e porzione transiente e' gestita mediante
l'int 2Fh. La main() effettua la scelta tra
disinstallazione e installazione; se ZAPTSR e' gia'
attivo in RAM viene invocata confirm() per richiedere
conferma. La func install() gestisce le operazioni
relative all'installazione; uninstall() quelle relative
alla disinstallazione.
Compilato sotto BORLAND C++ 1.01:
bcc -O -d -rd zaptsr.c
********************/
#pragma inline
#pragma warn -pia
#pragma warn -rvl
#pragma -k+ // il codice e' scritto per TURBO C++ 1.01
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#define PRG "ZAPTSR" /* nome del programma */
#define REL "1.0" /* versione */
#define YR "1991" /* anno di compilazione */
#define HEY_YOU 0xB3 /* byte riconoscimento chiamata all'int 2Fh */
#define HERE_I_AM 0xA1C9 /* risposta per gia' presente in RAM */
#define HANDSHAKE 0x00 /* richiesta se gia' presente in RAM */
#define UNINSTALL 0x01 /* richiesta disinstallazione all'int 2Fh */
#define UNINST_OPT '*' /* opzione richiesta disinstallazione */
#define ALLOC_OPT '+' /* opz. allocazione blocchi precedenti */
#define CNT_SPC 2 /* bytes occupati dal numero di copia install. */
#define MCB_SPC 2 /* bytes occupati dall'ind.seg. del MCB resid. */
#define VEC_SPC 1024 /* bytes occupati dalla tavola dei vettori */
#define FALSE NULL /* booleano per falso */
#define TRUE (!FALSE) /* booleano per vero */
#define LASTBLK 'Z' /* segnala ultimo blocco DOS di memoria */
const char *CopyRight = \
PRG": TSRs controller -"REL"- Barninga_Z! "YR".\n\
";
const char *ConfirmMsg = \
PRG": Already active; install copy #%u (Y/N)? \
";
const char *EnvErrMsg = \
PRG": Couldn't release environment block; memory error.\n\
";
const char *InstMsg = \
PRG": Copy #%u installed; RAM status and vectors table saved.\n\
NOTE: Installing "PRG" with a plus sign ('+') as command line\n\
parameter will protect lower MCBs.\n\
NOTE: All TSRs loaded after this copy of "PRG" (and "PRG" also)\n\
will be unloaded by invoking "PRG" with an asterisk ('*')\n\
as command line parameter.\n\
";
const char *UnInstMsg = \
PRG": Copy #%u uninstalled; vectors restored and RAM freed up.\n\
";
const char *UnInstErrMsg = \
PRG": Cannot uninstall; not active in RAM.\n\
DOS errorlevel set to 1.\n\
";
struct MCB { /* struttura per la gestione dei Memory Control Blocks */
char pos; /* 'Z' = ultimo blocco; 'M' = non ultimo */
unsigned psp; /* PSP del programma proprietario del blocco */
unsigned dim; /* dimensione del blocco in paragrafi */
char res[3]; /* riservato al DOS */
char name[8]; /* nome progr. se DOS >= 4.0; altrimenti non usato */
};
void _restorezero(void); /* non e' dichiarata negli include di bcc */
/********************
GData(): funzione dummy; lo spazio riservato nel code segment dalle
db e' utilizzato per memorizzare il numero della copia, l'indir.
di seg. del MCB della porzione residente e la tavola dei vettori.
L'opcode dell'istruzione RETF rappresenta un byte (in eccesso) in
coda allo spazio riservato ai dati globali.
Se si compilasse (sempre con TC++ 1.0 o successivi) ma senza la
opzione -k- lo spazio disponibile comprenderebbe 5 bytes in eccesso
(gli opcodes per la gestione dello stack piu' la RETF) di cui 3 in
testa. Questi ultimi non rappresenterebbero un problema in quanto i
dati memorizzati in GData() non devono essere inizializzati dal
compilatore, ma lo sono a run-time.
********************/
void far GData(void)
{
asm {
db CNT_SPC dup(?); /* numero della copia installata */
db MCB_SPC dup(?); /* ind. di seg. del MCB della parte residente */
db VEC_SPC dup(?); /* tavola dei vettori */
}
}
/********************
new2Fh(): gestore dell'int 2Fh. Utilizzato per le comunicazioni tra
parte transiente e parte residente. Se non riconosce in AH il
segnale della parte transiente (HEY_YOU) concatena il gestore
precedente il cui indirizzo e' prelevato direttamente nella copia
di tavola dei vettori contenuta in GData(). Altrimenti analizza AL
per determinare quale servizio e' richiesto dalla porzione
transiente. Sono riconosciuti due servizi:
1) HANDSHAKE: la parte transiente richiede se vi e' una copia di
ZAPTSR gia' attiva in RAM; new2Fh() restituisce in AX la parola
d'ordine quale risposta affermativa e in DX il numero della
copia.
2) UNINSTALL: la parte transiente richiede la disinstallazione
dell'ultima copia caricata; new2Fh() restituisce in AX:DX
l'indirizzo della GData() residente. La convenzione generale
per la restituzione di double words da funzioni è DX:AX,
sacrificata in questo caso per ottenere codice piu' compatto
efficiente.
********************/
void far new2Fh(void)
{
asm {
pop bp;
cmp ah,HEY_YOU;
jne CHAIN;
cmp al,HANDSHAKE;
jne NO_HANDSHAKE;
push ds;
mov ax,seg GData;
mov ds,ax;
mov bx,offset GData;
mov dx,ds:word ptr[bx]; /* DX = numero di copia */
mov ax,HERE_I_AM; /* AX = parola d'ordine */
pop ds;
iret;
}
NO_HANDSHAKE:
asm {
cmp al,UNINSTALL;
jne NO_UNINSTALL;
mov ax,seg GData;
mov dx,offset GData; /* AX:DX = indirizzo di GData() residente */
iret;
}
NO_UNINSTALL:
CHAIN:
asm jmp dword ptr GData+MCB_SPC+CNT_SPC+(4*2Fh);
}
/********************
releaseEnv(): libera il blocco di memoria allocato dal DOS alla copia
di environment creata per ZAPTSR.
********************/
void releaseEnv(void)
{
extern unsigned _envseg;
if(freemem(_envseg))
printf(EnvErrMsg);
}
/********************
restoredata(): ripristina i vettori di interrupt copiandoli dalla
GData() residente (di cui ottiene l'indirizzo via int 2Fh) alla
tavola dei vettori. Restituisce l'indirizzo di segmento del MCB
della parte residente.
********************/
unsigned restoredata(void)
{
asm {
push ds;
mov al,UNINSTALL;
mov ah,HEY_YOU;
int 2Fh;
cli;
mov ds,ax;
mov si,dx; /* DS:SI punta a GData residente */
add si,CNT_SPC; /* salta il n. di copia */
mov bx,ds:word ptr [si];
add si,MCB_SPC;
xor ax,ax;
mov es,ax;
xor di,di; /* ES:DI punta a tavola vettori */
mov cx,512;
rep movsw; /* ripristina tavola vettori */
sti;
pop ds;
}
return(_BX);
}
/********************
getfirstmcb(): ottiene dal DOS l'indirizzo del primo MCB in RAM.
ATTENZIONE: si basa su un servizio non documentato dell'in 21h
********************/
unsigned getfirstmcb(void)
{
asm {
mov ah,52h;
int 21h;
mov ax,es:[bx-2];
}
return(_AX);
}
/********************
uninstall(): pilota le oprazioni di disinstallazione. Da restoredata()
ottiene l'indirizzo di segmento della parte residente; da
getfirstmcb() ottiene l'indirizzo di segmento del primo MCB nella
RAM. Questo e' il punto di partenza per un ciclo in cui
uninstall() libera (azzrandone nel MCB l'indirizzo del PSP
proprietario) tutti i blocchi appartenenti a PSP residenti ad
indirizzi successivi a quello del MCB della parte residente.
Con questa tecnica sfuggono alla disinstallazione i TSR
eventualmente caricati ad inidirizzi inferiori a quello della
parte residente, quantunque dopo di essa cronologicamente (evento
raro, indicatore di problemi di allocazione).
********************/
void uninstall(unsigned cnt)
{
register resMCB, mcb;
resMCB = restoredata();
mcb = getfirstmcb();
do {
mcb += ((struct MCB far *)MK_FP(mcb,0))->dim+1;
if(((struct MCB far *)MK_FP(mcb,0))->psp > resMCB)
((struct MCB far *)MK_FP(mcb,0))->psp = 0;
} while(((struct MCB far *)MK_FP(mcb,0))->pos != LASTBLK);
printf(UnInstMsg,cnt);
}
/********************
savedata(): effettua il salvataggio dei dati nella GData(). Sono
copiati, nell'ordine, il numero della copia di ZAPTSR in fase di
installazione, l'indirizzo di segmento del MCB del medesimo e la
tavola dei vettori.
********************/
void savedata(unsigned cnt)
{
asm {
push ds;
cli;
cld;
mov ax,seg GData;
mov es,ax;
mov di,offset GData;
mov ax,cnt; /* salva numero di copia */
stosw;
mov ax,_psp;
dec ax; /* salva segmento MCB */
stosw;
xor ax,ax;
mov ds,ax;
xor si,si;
mov cx,512; /* salva tavola vettori */
rep movsw;
sti;
pop ds;
}
}
/********************
install(): pilota le operazioni di installazione. Invoca
_restorezero(), definita nello startup code, per ripristinare i
vettori eventualmente agganciati dallo startup code medesimo;
invoca savedata() passandole come parametro il numero della copia
di ZAPTSR in via di installazione; invoca releaseEnv() per
disallocare il blocco dell'environment; se e' richiesta l'opzione
ALLOC_OPT sulla command line install() alloca a ZAPTSR tutti i
blocchi di RAM liberi aventi indirizzo inferiore a quello di
ZAPTSR stesso, per evitare che TSRs invocati successivamente
vi si installino, occultandosi. Infine attiva il gestore
dell'int 2Fh new2Fh() mediante setvect() e installa ZAPTSR
chiamando direttamente l'int 21h, servizio 31h (non e' usata
keep() per evitare una nuova chiamata alla _restorezero()). I
paragrafi residenti sono calcolati in modo da allocare solo la RAM
indispensabile a GData() e new2Fh(). Notare che non viene salvato
il vettore originale dell'int 2Fh con getvect() in quanto esso e'
comunque presente nella copia della tavola dei vettori generata
con savedata().
********************/
void install(unsigned cnt,unsigned mem)
{
register mcb;
_restorezero(); /* ripristina vettori 00h-06h*/
savedata(cnt);
releaseEnv();
if(mem)
for(mcb = getfirstmcb(); mcb < (_psp-1); mcb +=
((struct MCB far *)MK_FP(mcb,0))->dim+1)
if(!((struct MCB far *)MK_FP(mcb,0))->psp)
((struct MCB far *)MK_FP(mcb,0))->psp = _psp;
setvect(0x2F,(void(interrupt *)())new2Fh);
printf(InstMsg,cnt);
_DX = FP_SEG(releaseEnv)+FP_OFF(releaseEnv)/16+1-_psp;
_AX = 0x3100;
geninterrupt(0x21);
}
/********************
AreYouThere(): invoca l'int 2Fh per verificare se e' gia' attiva in
RAM un'altra copia di ZAPTSR. Solo in questo caso, infatti, in AX
e' restituito dall'int 2Fh (cioe' dalla new2Fh() residente) il
valore HERE_I_AM, che funge da parola d'ordine. Allora DX contiene
il numero della copia di ZAPTSR che ha risposto (l'ultima
caricata).
********************/
unsigned AreYouThere(void)
{
_AH = HEY_YOU;
_AL = HANDSHAKE;
geninterrupt(0x2F);
if(_AX == HERE_I_AM)
return(_DX);
return(0);
}
/********************
confirm(): utilizzata per chiedere all'utente la conferma
dell'intenzione di installare una ulteriore copia di ZAPTSR quando
ve n'e' gia' una (o piu' di una) attiva in RAM.
********************/
int confirm(unsigned cnt)
{
int c;
do {
printf(ConfirmMsg,cnt);
printf("%c\n",c = getch());
switch(c) {
case 'N':
case 'n':
return(FALSE);
case 'Y':
case 'y':
return(TRUE);
}
} while(TRUE);
}
/********************
main(): distingue tra richiesta di installazione o disinstallazione e
intraprende le azioni opportune. Se il primo (o unico) carattere
del primo (o unico) parametro della command line e' un asterisco,
main() procede alla disinstallazione. In qualunque altro caso e'
effettuata l'installazione. Il registro DOS errorlevel contiene 0
se ZAPTSR e' stato installato o disinstallato regolarmente; 1 se
e' stata tentata una disinstallazione senza che ZAPTSR fosse
attivo in RAM.
********************/
int main(int argc,char **argv)
{
register cnt = 0, mem = FALSE;
printf(CopyRight);
if(argc > 1) {
if(*argv[1] == UNINST_OPT)
if(cnt = AreYouThere()) {
uninstall(cnt);
return(0);
}
else {
printf(UnInstErrMsg);
return(1);
}
if(*argv[1] == ALLOC_OPT)
mem = TRUE;
}
if(cnt = AreYouThere())
if(!confirm(cnt+1))
return(0);
install(cnt+1,mem);
return(0);
}
I commenti inseriti nel listato rendono superfluo dilungarsi nella descrizione degli algoritmi; è invece opportuno evidenziare le due limitazioni di cui ZAPTSR soffre. In primo luogo esso controlla solamente i 640 Kb di memoria convenzionale: rimane esclusa la upper memory, resa disponibile tra i 640 Kb e il primo Mb da alcuni gestori di memoria estesa/espansa nonché dal DOS 5.0. Inoltre, la RAM allocata a programmi TSR installati dopo ZAPTSR, ma ad indirizzi di memoria inferiori, non viene liberata: detti programmi sono solamente disattivati mediante il ripristino dei vettori di interrupt originali.
Il programma deve essere compilato con l'opzione k+, in quanto le parti scritte in inline assembly tengono conto del codice generato dal compilatore per la gestione standard dello stack anche nelle funzioni che non prendono parametri. Ciò vale anche con riferimento alla GData(), che deve riservare ai dati un numero esatto di byte.
Il lettore volonteroso (e temerario) può tentare di realizzare un programma che operi come un vero e proprio controllore del sistema, intercettando i TSR di volta in volta caricati per essere in grado di individuarli e disinstallarli in ogni caso: la tabella che segue contiene alcuni suggerimenti:
INTERRUPT E SERVIZI DI CARICAMENTO E TERMINAZIONE DEI PROGRAMMI
Necessario per consentire la comunicazione tra porzione transiente e porzione residente. | ||
Utilizzato dal DOS per caricare da disco a memoria i programmi.
Se AL = 0 il programma viene eseguito. In tal caso DS:DX punta al nome; la word ad ES:BX è l'indirizzo di segmento dell'environment (se è 0 il programma eseguito condivide l'environment di quello chiamante). Prima di concatenare il gestore originale occorre salvare la tavola dei vettori. | ||
Il programma termina senza rimanere residente: la tabella dei TSR non necessita aggiornamenti. | ||
Come il precedente | ||
Come il precedente | ||
Il programma termina e rimane residente in memoria.
Il PSP del programma che sta per essere installato è quello attuale, a meno che il "controllore" lo abbia sostituito con il proprio attivandosi. La tabella dei TSR deve essere aggiornata con i dati raccolti intercettando l'int 21h servizio 4Bh. | ||
Come il precedente |
Non ci ho capito niente! Ricominciamo...