??
字號:
★★常駐內存程序[討論]★★
作者:fanoble Luckylai
也許大家不理解意思。
駐留內存的意思就是
1)不能影響其他程序的運行。
2)自我復制。
3)某種情況下進行破壞(比如4月16日)。
對于DOS程序(病毒)來說,你起碼要篡改一個中斷。然后在你的中斷程序中把真實的中斷做一次,就神不知鬼不覺了
下面我以篡改鍵盤中斷的病毒解釋一個例子。
由于WINDOWS的保護,所以這個程序是無法運行的(可能在DOS下也有問題,因為程序結束,內存被收回,病毒也就被KILL了這點請fanoble解決),只能提供一個思路。
建議:
1)最好使用匯編來寫,資源很節約,而且可以把病毒程序自身保護好。
2)把中斷程序放到系統一般不會使用的內存區域。
匯編這方面fanoble比較強,讓他來寫肯定沒問題。
文件key.c
//++++++++++++++++++++
//攔截了9號中斷,也就是鍵盤中斷。這個中斷對于我們VCOK的人很熟悉的。在《電腦游戲編程入門 (DOS)》
//《DOS游戲編程二十一條》(C高級版有)都篡改了這個中斷。
//觸發條件是按鍵100下。
#define MAX_COUNTER 100
long key_counter=0;
void interrupt far (*OldInt9Handler)(__CPPARGS); //保存舊的鍵盤中斷
//3.新的鍵盤中斷程序:也就是病毒主體
void far interrupt NewInt9(__CPPARGS)
{
*OldInt9Handler();//運行舊的鍵盤中斷
if(key_counter>MAX_COUNTER)//當計數值大于100病毒爆發
{
key_counter=0;
//printf("hello! you will be killed");
//插入你的破壞程序
}
}
//1.安裝新的鍵盤中斷程序的函數:
void InstallKeyboard(void)
{
OldInt9Handler=getvect(9);
setvect(9,NewInt9);
}
//2.恢復舊的鍵盤中斷程序的函數:
void ShutDownKeyboard(void)
{
setvect(9,OldInt9Handler);
}
void main(void)
{
InstallKeyboard();
}
/--------------------------------------
以下fanoble著:
呵呵,承蒙斑竹夸獎,fanoble寫了一個小的駐留程序
/***************************************************************
* A small resident program with C code *
*==============================================================*
* (C) Copyright by fanoble QQ:87430545 Mail:fanoble@yeah.net *
***************************************************************/
# include <dos.h>
# define MUTEX_NAME "fanoble" /* 用于檢測駐留的互斥 */
typedef struct
{
char Flag; /* 標志 'M' 或 'Z' */
unsigned Owner; /* 內存擁有者 */
unsigned Size; /* 內存大小(節) */
char Res[3]; /* 保留 */
char Name[8]; /* 名稱 */
}MCB;
extern unsigned _envseg; /* 環境段 */
unsigned char Color = 1; /* 控制字符顏色 */
struct REGPACK r;
void interrupt (*OldInt9)(); /* 原中斷 */
void interrupt NewInt9() /* 新中斷 */
{
(*OldInt9)(); /* 調用原中斷 */
disable();
r.r_ax = 0x0920;
r.r_cx = 1;
r.r_bx = Color;
intr(0x10, &r); /* 設置字符顏色 */
Color++; /* 顏色加1 */
if (16 == Color)
{
Color = 1;
}
r.r_ax = 0x0200;
intr(0x16, &r); /* 檢測Ctrl和Alt */
r.r_ax &= 0x000C;
if (0x000C == r.r_ax) /* Ctrl + Alt按下 */
{
setvect(9, OldInt9); /* 恢復原中斷 */
poke(_psp - 1, 1, 0); /* 釋放內存,注意:為
防止DOS重入,不能
用freemem釋放 */
poke(_psp - 1, 8, 0); /* 刪除互斥體 */
}
enable();
}
/***************************************************************
* 函數名稱: CheckResident *
* 函數作用: 檢查是否駐留 *
* 輸入參數: 無 *
* 返 回 值: 0 -> 未駐留 *
* 1 -> 駐留 *
***************************************************************/
int CheckResident()
{
MCB far* pMCB;
unsigned nSegment;
char szName[8];
/* 得到第一個MCB */
r.r_ax = 0x5200;
intr(0x21, &r);
nSegment = peek(r.r_es, r.r_bx - 2);
pMCB = (MCB far*)MK_FP(nSegment, 0);
for (;;)
{
/* 拷貝互斥體 */
movedata(nSegment, 8, _DS, szName, 8);
/* 判斷互斥體 */
if (0 == strcmp(szName, MUTEX_NAME))
{
return 1;
}
/* 判斷是否最后一個MCB */
if ('Z' == pMCB->Flag)
{
break;
}
/* 得到下一個MCB */
nSegment += pMCB->Size + 1;
pMCB = (MCB far*)MK_FP(nSegment, 0);
}
return 0;
}
void main()
{
unsigned nSize;
MCB far* pMCB;
/* 檢測是否駐留 */
if (CheckResident())
{
printf("Program has already been loaded!");
exit(0);
}
/* 得到本程序所對應的MCB */
pMCB = (MCB far*)MK_FP(_psp - 1, 0);
/* 創建互斥體 */
movedata(_DS, MUTEX_NAME, _psp - 1, 8, 8);
/* 設置新中斷 */
OldInt9 = getvect(9);
disable();
setvect(9, NewInt9);
enable();
/* 釋放環境段內存 */
freemem(_envseg);
/* 駐留退出 */
printf("Program resident OK!\n");
printf("To quit, press Ctrl + Alt.\n");
printf("You can hit a key now :)");
keep(0, pMCB->Size);
}
/***************************************************************
* END OF FILE *
***************************************************************/
程序運行駐留以后,在DOS窗口內每按一個鍵就換一次顏色,按Ctrl+Alt退出。
程序運行前后以及釋放后的內存大小可以用MEM來查看。
下面是附帶的一個小tool,可以查看MCB。
/***************************************************************
* A small mcb viewer with C code *
*==============================================================*
* (C) Copyright by fanoble QQ:87430545 Mail:fanoble@yeah.net *
***************************************************************/
# include <dos.h>
typedef struct
{
char Flag;
unsigned Owner;
unsigned Size;
char Res[3];
char Name[8];
}MCB;
extern unsigned _envseg;
void main()
{
MCB far* pMCB;
struct REGPACK r;
unsigned nSize;
unsigned nSegment;
char szName[8];
nSize = peek(_psp - 1, 3);
printf("Size = %04X\n", nSize);
printf("PSP = %04X\n", _psp);
printf("ENV = %04X\n", _envseg);
/*
freemem(_envseg);
freemem(_psp);
*/
r.r_ax = 0x5200;
intr(0x21, &r);
nSegment = peek(r.r_es, r.r_bx - 2);
pMCB = (MCB far*)MK_FP(nSegment, 0);
printf("Flag\tOwner\tSize\tName\n");
for (;;)
{
movedata(nSegment, 8, _DS, szName, 8);
printf("%c\t%04X\t%04X\t%s\n", pMCB->Flag, pMCB->Owner, pMCB->Size, szName);
if ('Z' == pMCB->Flag)
{
break;
}
nSegment += pMCB->Size + 1;
pMCB = (MCB far*)MK_FP(nSegment, 0);
}
}
_envseg是環境段的段值,從TC的Start Up Code 得到:
c0.asm:
PSPHigh equ 00002h
PSPEnv equ 0002ch
mov bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr
mov bx, ds:[PSPEnv] BX = Environment Segment address
mov ds, dx
mov _version@, ax Keep major and minor version number
mov _psp@, es Keep Program Segment Prefix address
mov _envseg@, bx Keep Environment Segment address
其實就是放在_psp段的0x2C處,用peek(_psp, 0x2C)也可以得到.
DOS為每個程序創建2個內存塊,一個是環境塊,一般很小,另一個緊接
環境塊的是進程塊即_psp,2個塊都有MCB.程序運行退出時,DOS會把2個MCB的
Owner設置為0,表示這塊內存是空閑的,然后就可以回收再利用了.
程序用keep來駐留退出的話,2個塊都不釋放,在這兒環境塊沒用了,就先
釋放掉,當然也可以到最后和psp一起釋放.
PS:對free和keep做一下說明
[freemem]
int _Cdecl freemem (unsigned segx);
釋放內存.
segx是要釋放的段值.
freemem的執行過程是這樣的:
_freemem:
push bp
mov bp,sp
mov ah,49h
mov es,word ptr [bp+004h]
int 21h
jc $L1
xor ax,ax
jmp short $L2
$L1: push ax
call __IOERROR
jmp short $L2
$L2: pop bp
ret
仍然是DOS中斷,加了出錯處理.我估計這個中斷所做的事情就是把MCB的Owner
設置為0,的確如此,這個可以驗證.所以不用中斷釋放內存的話就變的很容易:
(1) 將段值減1,得到MCB段值.
(2) 將MCB段偏移為1的一個字,即Owner,設置為0.
釋放_psp就是這樣: poke(_psp - 1, 1, 0);
[keep]
void _Cdecl keep (unsigned char status, unsigned size);
將_psp段駐留size個節并退出.
status是退出返回給系統的值,象main函數的return 0一樣.
size 是駐留的大小,按節(每節16字節,一個段)計算.
keep的執行過程是這樣的:
_keep: push bp
mov bp,sp
call __restorezero
mov dx,[bp+006h]
mov al,[bp+004h]
mov ah,31h
int 21h
pop bp
ret
把_psp駐留了,_envseg雖然沒有去管,但并沒有釋放,這也是退出前手工釋放
_envseg的原因.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -