📄
字号:
★★常驻内存程序[讨论]★★
作者: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 + -