缥缈峰一座01号

梁二伯的窝

  DonewsBlog  |  Donews首页  |  Donews社区  |  Donews邮箱  |  我的首页  |  联系作者  |  聚合   |  登录
  86篇文章 :: 0篇收藏:: 27篇评论:: 0个Trackbacks

公告

Equality && Free

文章

收藏

相册

存档


正在读取评论……


作者:CoolQ  <qufuping@ercist.iscas.ac.cn>
出处:http://www.linuxforum.net/forum/showflat.php?Cat=&Board=security&Number=
日期:2005-04-04

|=-------------[ ways to hide files in ext2/3 filesystem ]----------------=|
|=------------------------------------------------------------------------=|
|=---------------[ CoolQ  <qufuping@ercist.iscas.ac.cn> ]-----------------=|
|=------------------------------------------------------------------------=|

0 - 前言

1 - EXT2/3 文件格式简介
        1.1 整体部局
        1.2 ext2_super_block
        1.3 ext2_group_desc
        1.4 ext2_inode
        1.5 ext2_dir_entry
        1.6 调试工具

2 - EXT2/3 文件的创建与删除
        2.1 文件创建
        2.2 文件删除
        2.3 目录项的处理

3 - 不彻底的隐藏方法
        3.1 隐藏
        3.2 恢复
        3.3 优点/缺点

4 - FSCK

5 - Anti FSCK
        5.1 对策
        5.2 优点/缺点

6 - 结尾

7 - 参考

8 - 第三部分的实现代码(PoC)

--[ 0 - 前言

Ext2/3作为Linux下默认的文件系统,被各种Distribution广泛的使用。在攻防中,
文件隐藏是一个很重要的环节,但是我在网上搜索"ext2 file hide",没有发现什么
好的结果。前一阵看过一个对bmap分析的GCIA的文章[1],觉得这个工具限制太大,
不光隐藏的内容有4K的限制,而且依附的文件不能被编辑,否则隐藏的信息会丢失。

本文通过对EXT2/3文件系统的分析,给出两种隐藏文件的方法,不算完美,可行性尚可.

--[ 1 - EXT2/3 文件格式简介

--[ 1.1 整体部局
EXT2/3跟传统的Unix文件系统很相似,分成几个部分:
o 启动扇区 0x0 - 0x3ff
o 超级块 0x400 - 0xfff(不足用0填充)
o 块组描述符 0x1000 - ... (以块为单位对齐)
o 多个块组
  块组里有文件系统的大部分信息,包括超级块和块组描述符的备份(单序号),块组
  内块的位图,块组内inode的位图,块组内的inode表,数据区

--[ 1.2 ext2_super_block
struct ext2_super_block
  {
    __u32 s_inodes_count;    /* Inodes count */
    __u32 s_blocks_count;    /* Blocks count */
    __u32 s_r_blocks_count;    /* Reserved blocks count */
    __u32 s_free_blocks_count;    /* Free blocks count */
    __u32 s_free_inodes_count;    /* Free inodes count */
    __u32 s_first_data_block;    /* First Data Block */
    __u32 s_log_block_size;    /* Block size */
    __s32 s_log_frag_size;    /* Fragment size */
    __u32 s_blocks_per_group;    /* # Blocks per group */
    __u32 s_frags_per_group;    /* # Fragments per group */
    __u32 s_inodes_per_group;    /* # Inodes per group */
    ...
    __u16 s_magic;        /* Magic signature */
    ...
    __u32 s_reserved[235];    /* Padding to the end of the block */
  };

--[ 1.3 ext2_group_desc
struct ext2_group_desc
  {
    __u32 bg_block_bitmap;    /* Blocks bitmap block */
    __u32 bg_inode_bitmap;    /* Inodes bitmap block */
    __u32 bg_inode_table;    /* Inodes table block */
    ...
  };

--[ 1.4 ext2_inode
struct ext2_inode
  {
    __u16 i_mode;        /* File mode */
    __u16 i_uid;        /* Owner Uid */
    __u32 i_size;        /* 4: Size in bytes */
    ...
    __u16 i_links_count;    /* 24: Links count */
    __u32 i_blocks;        /* Blocks count */
    ...
    __u32 i_block[EXT2_N_BLOCKS];    /* 40: Pointers to blocks */
    ...
  };

--[ 1.5 ext2_dir_entry
struct ext2_dir_entry
  {
    __u32 inode;        /* Inode number */
    __u16 rec_len;        /* Directory entry length */
    __u8 name_len;        /* Name length */
    __u8 file_type;
    char name[EXT2_NAME_LEN];    /* File name */
  };

--[ 1.6 调试工具
常用的调试工具是[2]中带的dumpe2fs和debugfs,其中以debugfs最为方便,读者可以
用debugfs /dev/hdaX来查看一下自己硬盘的文件系统信息,常用的命令有:
stats、stat、cat、dump、……

有关ext2的详细信息,大家可以看一下[3], 至于ext3,与ext2的差别就是ext3利用一
个保留的inode来记录文件系统的操作(也就是journal),对本文没有任何影响。

--[ 2 - EXT2/3 文件的创建与删除
现在我们来看一下内核是如何实现文件的创建和删除的.

--[ 2.1 文件创建
文件系统会首先从inode位图中找到空余的inode,然后将相应的inode表中的结构赋值
,之后会在文件所在的目录中添加一个目录项,并将文件目录中所在inode的连接数加
一。

--[ 2.2 文件删除
删除文件在父目录中的目录项,将父目录所在的inode连接数减一,将文件所使用的块
释放(修改块的位图),将文件的inode释放,并修改inode的位图。

--[ 2.3 目录项的处理
ext2_dir_entry的结构前面已经说过,这里举一个例子,假设我们需要建立一个名为
AA的目录项,那么inode就是AA文件的inode,name_len为AA按四字节对齐的结果,即
4,name为0x41410000(注意name中的0x00是对齐的结果,而不是字符串结尾的0x0).
这里需要解释的是rec_len这个字段,如果AA后边还有别的文件,那么rec_len = 4
(__u32 inode) + 4(__u16 + __u8 + __u8) + 4(0x41410000) = 12
我们再看看当删除一个目录项时内核[4]是如何做得:
/usr/src/linux/fs/ext2/dir.c
/*
* ext2_delete_entry deletes a directory entry by merging it with the
* previous entry. Page is up-to-date. Releases the page.
*/
    unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len);
        ...
    while ((char*)de < (char*)dir) {
        pde = de;
        de = ext2_next_entry(de);
    }
    if (pde)
        from = (char*)pde - (char*)page_address(page);
    ...
    if (pde)
        pde->rec_len = cpu_to_le16(to-from);
可见,删除一个目录项p,实际上就是将前一个目录项prev->rec_len += p->rec_len
同样,当要添加一个目录项时,内核也会遍历每一个目录项,看p->rec_len - p->
name_len的空间是否能放入新的目录项,如果可以,就会使用这些空间,并将
p->rec_len = ALIGN_4(p->name_len) + 8,并将剩余的长度赋值给新目录项的rec_len

--[ 3 - 不彻底的隐藏方法

--[ 3.1 隐藏
知道了内核的步骤,我们可以自己通过访问磁盘所在的原始设备,跳过某些操作,单纯
删除目录项而保留inode,达到隐藏文件的目的,具体的步骤是:
o 获取隐藏文件的inode号,以及父目录的inode号
o 读取父目录所在磁盘的超级块、块组描述符
o 根据前面的信息找到父目录inode号所在的inode结构,并读取相应的block
o 在读取的block从头开始查找每一个ext2_dir_entry结构,并检查相应的inode是不
  是需要隐藏文件的inode
o 如果是,则将前一个ext2_dir_entry的prev->rec_len += p->rec_len;如果不是,
  继续查找
还有一点需要注意的是,这几步完成以后,p的内容没有任何变化,只是与prev合并,
那么此时p->rec_len是否还有用呢?如果p本身之前没有与后边的目录项合并,那么
一定有p->rec_len == EXT2_DIR_REC_LEN(p->name_len);否则,p->rec_len也一定
经过 += ..->rec_len的情形。但无论如何,p->rec_len的信息已经包含于prev->
rec_len中,因此,我们在恢复中,只要能够确定(char *)p - (char *)prev,就能
将原来的prev->rec_len和p->rec_len恢复出来。因此我们最后一步是
o 将p->rec_len设置为一个独特的magic number,例如0xdead

--[ 3.2 恢复
恢复过程,并不是隐藏的单纯逆过程,前几步还是一样的:
o 获取恢复文件所在目录的inode
o 读取目录所在磁盘的超级块、块组描述符
o 根据前面的信息找到目录inode号所在的inode结构,并读取相应的block
之后就是如何确定block里哪些是隐藏的目录项了,我们从头开始遍历:
现在我们手头上有一个目录项p, 如果p->rec_len==EXT2_DIR_REC_LEN(p->name_len)
那么p中一定不含有隐藏的内容,反过来呢?有几种可能
|-> [p entry][隐藏的entry]
|-> [p entry][一个或多个删除的entry][隐藏的entry]
|-> [p entry][一个或多个删除的entry][无用数据][隐藏的entry]
|-> ……
无用数据是怎么形成的呢?假设原来有三个目录项p1, p2, p3,长度分别是12,24,12
我们用I/i表示一个Int,那么就是
[p1][p2][p3] -> [III][IIIIII][III]
现在把p2删除,再添加一个p4,大小为16,那么现在就成了
[p1][p4][p3] -> [III][IIIIii][III]
这样"ii"就成了无用的数据
这个无用数据在我们恢复的过程中造成了很大的麻烦。有了这个无用数据,我们就
不好确定隐藏目录项的起点。因此,我们干脆更简单一些,直接对p的剩余部分,搜
索每一个int i,如果(i & 0xffff) == maigc,那么说明这一项就是隐藏的目录项,
将p一分为二。
需要指出的是,这个magic一定要足够独特,否则会与无用数据冲突。不过在隐藏
之前,我们可以遍历一下block,确定该magic没有出现过。用户也可以自己指定,
最好选择4对齐的、ASCII不常见的数值。

--[ 3.3 优点/缺点
优点:可以隐藏任意多数量,任意大小的的文件,恢复的时候,只要记住目录名即可
缺点:隐藏完文件后,目录如果建立新的文件,有可能会将隐藏的目录项覆盖,从而
      无法恢复(不过Linux系统下有很多不会变动的目录,例如/usr/share/doc/
      vim-6.2);另外,由于隐藏文件所对应的inode实际上并没有消失,因此,如
      果对文件系统进行完整性检查,会有orphan inode出现,其实就是我们隐藏的
      文件。

--[ 4 - FSCK
fsck是e2fsprogs的一个工具,在一般的Linux主机上都有安装。当主机没有正常关机
,分区所在的超级块标记为脏,或者根分区存在.autofsck,或者指定的时间/次数
没有执行fsck,fsck都会在启动时运行。
e2fsck是ext2的fsck,运行时会进行文件系统的检测,并试图尽力恢复错误,检测
一共有5个pass:
Pass 1: 获取inode和block的位图信息,读取所有的inode结构,判断inode各个字段
        的合法性(例如文件权限,块的取值范围……)。这个Pass所耗的时间最常,
        程序会尝试将尽可能多的数据读如内存,以避免多次读取磁盘的耗费。
Pass 2: 检查目录,将所有的目录块读如内存,检查相应的合法性(例如"."是否指向
        自己)。
Pass 3: 检查目录的连接性,任何不能返回到/的目录都将报错
Pass 4: 检查所有inode的连接计数
Pass 5: 检查文件系统的综合信息
对FSCK 5个Pass的描述,请参见[3]

--[ 5 - Anti FSCK

--[ 5.1 对策
3中的方法,由于隐藏文件对应的inode并没有释放,因此在fsck的Pass4会作为orphan
inode被连接到/lost+found中去,也就是说,该inode没有文件与之相对应,但是我们
却不能将该inode完全释放,否则会丢失隐藏文件的块分布信息。这里我们可以利用
FSCK的一个Bug(其实也算不上)。
ext2/3在建立文件系统的时候,会将前十个inode作为保留inode,这些inode,即使
没有使用,在位图上也标柱为已使用,FSCK也并不检查这些inode.
我们需要做的是
隐藏)
o 从这十个inode中找出空余的inode
o 将隐藏文件的inode信息复制过来
o 将隐藏文件的inode释放,清除相应的inode位图
恢复)
o 从非保留inode中找到一个空余inode
o 将指定inode的内容赋值给该空余的inode,将相应inode位图置位,并清空指定
  inode。
o 在合适的位置建立一个目录项,取一个名字,修改父目录的连接计数

--[ 5.2 优点/缺点
优点:能够逃过FSCK的检测
缺点:隐藏的文件数受到限制(不能小于10个,例如ino=2代表"/"就不能使用),另外
      ,还需要记住隐藏时复制的保留inode号。

--[ 6 - 结尾
在本文结尾之时,无意中发现了Grugq的[5]和他在phrack上的[6],其实[6]的RuneFS就
是将5.1中使用的保留inode=0, 但是没涉及文件的操作;[5]中的WaffenFS就是保留的
inode=8;至于其中的KY FS,隐藏的大小有限制;至于Mule FS,比较复杂,大小有
限制,而且只能隐藏一个文件流。不过[5]中的KY FS和Mule FS想法比较巧妙,感兴
趣的读者可以研究一下

--[ 7 - 参考

[1] Tyler Hudak GCFA assignment
     <http://www.giac.org/certified_professionals/practicals/gcfa/0108.php>
[2] <http://e2fsprogs.sourceforge.net/>
[3] <http://e2fsprogs.sourceforge.net/ext2intro.html>
[4] linux source code <www.kernel.org>
[5] The Art of Defiling
     <http://packetstormsecurity.com/hitb04/hitb04-grugq.pdf>
[6] Defeating Forensic Analysis on Unix
     <http://www.phrack.org/show.php?p=59&a=6>

--[ 8 - 第三部分的实现代码(PoC)

|=------------------------------- ext.c ----------------------------------=|
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

#include "ext.h"
#include "utils.h"

static struct part_info     *part_info_list = NULL;
static struct ext2_inode    ino_buf;
static unsigned int        list_count = 0;

_syscall5(int,  _llseek, unsigned int, fd, unsigned long, hi,
    unsigned long, lo, loff_t *, res, unsigned int, wh);

int raw_read(int fd, loff_t offset, char *buf, unsigned int size)
{
    unsigned int    n, i;
    int        ret;
    loff_t        result = 0;
    
    n = size;
    i = 0;
    if(_llseek(fd, offset >> 32, offset & 0xffffffffULL, &result,SEEK_SET)
            == (-1))
        return -1;
    
    while(n > 0){
        ret = read(fd, buf + i, n);
        if(ret == -1)
            return i;
        i += ret;
        n -= ret;
    }
    return i;
}

int raw_write(int fd, loff_t offset, char *buf, unsigned int size)
{
    unsigned int    n, i;
    int        ret;
    loff_t        result = 0;

    n = size;
    i = 0;
    if(_llseek(fd, offset >> 32, offset & 0xffffffffULL, &result,SEEK_SET)
            == (-1))
        return -1;
    
    while(n > 0){
        ret = write(fd, buf + i, n);
        if(ret == -1)
            return i;
        i += ret;
        n -= ret;
    }
    return i;
}

struct part_info *get_partinfo_bydev(dev_t device_no)
{
    int        i;
    
    for(i = 0; i < list_count; i++)
        if(part_info_list[i].device_no == device_no)
            return &part_info_list[i];
    return NULL;
}

int alloc_partinfo_bydev(dev_t device_no)
{
    struct part_info     *tmp;
    char             *dev_name;
    int            fd, ret, i;
    unsigned int        group_nr;
    struct ext2_super_block *super;
    struct ext2_group_desc    **group_list;
    
    list_count++;
    
    tmp = realloc(part_info_list, list_count * sizeof(struct part_info));
    if(!tmp)
        goto err;

    part_info_list = tmp;
    dev_name = get_dev_name(device_no);
    if(!dev_name)
        goto err;
    
    fd = open(dev_name, O_RDWR);
    if(fd == -1)
        goto err1;
    
    super = malloc(sizeof(struct ext2_super_block));
    if(!super)
        goto err1;
            
    ret = raw_read(fd, (loff_t)0x400, (char *)super,
            sizeof(struct ext2_super_block));
    if(ret < sizeof(struct ext2_super_block))
        goto err2;
    
    group_nr = super->s_blocks_count / super->s_blocks_per_group;
    group_list = calloc(group_nr * sizeof(struct ext2_group_desc *), 1);
    if(!group_list)
        goto err2;
    
    for(i = 0; i < group_nr; i++){
        group_list[i] = malloc(sizeof(struct ext2_group_desc));
        ret = raw_read(
            fd,
            (loff_t)(0x1000 + i * sizeof(struct ext2_group_desc)),
            (char *)group_list[i],
            sizeof(struct ext2_group_desc)
            );
        if(ret < sizeof(struct ext2_group_desc))
                goto err3;
    }
    
    part_info_list[list_count - 1].fd         = fd;
    part_info_list[list_count - 1].device_no     = device_no;
    part_info_list[list_count - 1].super         = super;
    part_info_list[list_count - 1].group_nr     = group_nr;
    part_info_list[list_count - 1].group_list     = group_list;
    
    free(dev_name);
    return 0;
err3:
    for(i = 0; i < group_nr; i++)
        free(group_list[i]);
    free(group_list);
err2:
    free(super);
err1:
    free(dev_name);
err:
    list_count--;
    return -1;
}

int get_inode(struct part_info *part, int inode_nr, struct ext2_inode *ino)
{
    int             grp_idx, grp_ino_idx;
    loff_t           ino_offset;
    
    /* inode start with 1 */
    inode_nr--;

    grp_idx = inode_nr / part->super->s_inodes_per_group;
    grp_ino_idx = inode_nr % part->super->s_inodes_per_group;
    printf("EXT2_BLOCK_SIZE = %d\n", EXT2_BLOCK_SIZE(part->super));
    ino_offset = (loff_t)(part->group_list[grp_idx]->bg_inode_table)
            * EXT2_BLOCK_SIZE(part->super) +
            grp_ino_idx * sizeof(struct ext2_inode);
    
    if(raw_read(part->fd, ino_offset, (char *)ino,sizeof(struct ext2_inode))
        != sizeof(struct ext2_inode))
        return -1;
    return 0;

}

int put_inode(struct part_info *part, int inode_nr, struct ext2_inode *ino)
{
    int             grp_idx, grp_ino_idx;
    loff_t           ino_offset;

    /* inode start with 1 */
    inode_nr--;

    grp_idx = inode_nr / part->super->s_inodes_per_group;
    grp_ino_idx = inode_nr % part->super->s_inodes_per_group;
    ino_offset = (loff_t)(part->group_list[grp_idx]->bg_inode_table)
            * EXT2_BLOCK_SIZE(part->super) +
            grp_ino_idx * sizeof(struct ext2_inode);
    
    if(raw_write(part->fd, ino_offset,(char *)ino,sizeof(struct ext2_inode))
        != sizeof(struct ext2_inode))
        return -1;
    return 0;
}

int get_phy_block_nr(struct part_info *part, struct ext2_inode *ino,
        unsigned int block_nr)
{
    int            ret;
    loff_t            buf_offset;
    char            ind_buf[4096], dind_buf[4096], tind_buf[4096];
    struct ext2_super_block *s = part->super;
    
    /* direct block */
    if(block_nr < EXT2_NDIR_BLOCKS){
        ret = ino->i_block[block_nr];
        goto out;
    }

    /* indirect block */
    block_nr -= EXT2_NDIR_BLOCKS;
    
    if(block_nr < EXT2_ADDR_PER_BLOCK (s)){
        buf_offset = ino->i_block[EXT2_IND_BLOCK] * EXT2_BLOCK_SIZE(s);
        ret = raw_read(part->fd, buf_offset, ind_buf,
                EXT2_BLOCK_SIZE(s));
        if(ret != EXT2_BLOCK_SIZE(s))
            return -1;
        ret = ((__u32 *)ind_buf)[block_nr];
        goto out;
    }
    
    /* double indirect block */
    block_nr -= EXT2_ADDR_PER_BLOCK(s);
    
    if(block_nr < (1 << (EXT2_ADDR_PER_BLOCK_BITS(s) * 2))){
        buf_offset =     ino->i_block[EXT2_DIND_BLOCK] *
                EXT2_BLOCK_SIZE(s);
        ret = raw_read(part->fd, buf_offset, ind_buf,
                EXT2_BLOCK_SIZE(s));
        if(ret != EXT2_BLOCK_SIZE(s))
            return -1;
        buf_offset     = ((__u32 *)ind_buf)
                [block_nr >> EXT2_ADDR_PER_BLOCK_BITS(s)] *
                EXT2_BLOCK_SIZE(s);
        ret = raw_read(part->fd, buf_offset, dind_buf,
                EXT2_BLOCK_SIZE(s));
        if(ret != EXT2_BLOCK_SIZE(s))
            return -1;
        ret = ((__u32 *)dind_buf)
                [block_nr & (EXT2_ADDR_PER_BLOCK(s) - 1)];
        goto out;
    }

    /* triple indirect block */
    block_nr -= (1 << (EXT2_ADDR_PER_BLOCK_BITS(s) * 2));
    
    buf_offset = ino->i_block[EXT2_TIND_BLOCK] * EXT2_BLOCK_SIZE(s);
    ret = raw_read(part->fd, buf_offset, ind_buf, EXT2_BLOCK_SIZE(s));
    if(ret != EXT2_BLOCK_SIZE(s))
        return -1;
    buf_offset =     ((__u32 *)ind_buf)
            [block_nr >> (EXT2_ADDR_PER_BLOCK_BITS(s) * 2)] *
            EXT2_BLOCK_SIZE(s);
    ret = raw_read(part->fd, buf_offset, dind_buf, EXT2_BLOCK_SIZE(s));
    if(ret != EXT2_BLOCK_SIZE(s))
        return -1;
    buf_offset = ((__u32 *)dind_buf)
            [(block_nr >> EXT2_ADDR_PER_BLOCK_BITS(s))
            & (EXT2_ADDR_PER_BLOCK(s) - 1)] * EXT2_BLOCK_SIZE(s);
    ret = raw_read(part->fd, buf_offset, tind_buf, EXT2_BLOCK_SIZE(s));
    if(ret != EXT2_BLOCK_SIZE(s))
        return -1;
    ret = ((__u32 *)tind_buf)[block_nr & (EXT2_BLOCK_SIZE(s) - 1)];

out:
    return ret;
}

int get_block(struct part_info *part, struct ext2_inode *ino,
        unsigned int block_nr, char *block_buf)
{
    int        phy_block_nr;
    int        ret;
    loff_t        block_offset;

    phy_block_nr = get_phy_block_nr(part, ino, block_nr);
    if(phy_block_nr == -1)
        return -1;
    block_offset =     (loff_t)phy_block_nr *
            (loff_t)EXT2_BLOCK_SIZE(part->super);
    ret = raw_read(part->fd, block_offset, block_buf,
            EXT2_BLOCK_SIZE(part->super));
    if(ret != EXT2_BLOCK_SIZE(part->super))
        return -1;
    return 0;
}

int put_block(struct part_info *part, struct ext2_inode *ino,
        unsigned int block_nr, char *block_buf)
{
    int        phy_block_nr;
    int        ret;
    loff_t        buf_offset;

    phy_block_nr = get_phy_block_nr(part, ino, block_nr);
    if(phy_block_nr == -1)
        return -1;
    buf_offset =     (loff_t)phy_block_nr *
            (loff_t)EXT2_BLOCK_SIZE(part->super);
    ret = raw_write(part->fd, buf_offset, block_buf,
            EXT2_BLOCK_SIZE(part->super));
    if(ret != EXT2_BLOCK_SIZE(part->super))
        return -1;
    return 0;
}

int dir_buf_check_mag(char *dir_buf, unsigned int size, unsigned short magic)
{
    struct ext2_dir_entry *p;

    p = (struct ext2_dir_entry *)dir_buf;

    while((char *)p < dir_buf + size){
        if(p->rec_len == magic){
            fprintf(stdout, "magic 0x%x is not reliable, use -m to "
                    "choose another one.\n", magic);
            return -1;
        }
        p = (struct ext2_dir_entry *)((char *)p + p->rec_len);
    };

    return 0;
}
int dir_buf_hide(char *dir_buf, unsigned int size, __u32 ino,
        unsigned short magic, unsigned int *pos1, unsigned int *pos2)
{
    struct ext2_dir_entry    *p, *prev;
    int            find_flag = 0;
    
    prev = p = (struct ext2_dir_entry *)dir_buf;
    
    while((char *)p < dir_buf + size){
        if(p->inode == ino){
            prev->rec_len += p->rec_len;
            p->rec_len = magic;
            *pos1 = (char *)&(prev->rec_len) - dir_buf;
            *pos2 = (char *)&(p->rec_len) - dir_buf;
            find_flag = 1;
            break;
        }else{
            prev = p;
            p = (struct ext2_dir_entry *)((char *)p + p->rec_len);
        }
    };
    if(!find_flag)
        return -1;
    return 0;
}

int dir_buf_unhide(char *dir_buf, unsigned int size, unsigned short magic,
        unsigned int *pos1, unsigned int *pos2, int *unhide_cnt)
{
    struct ext2_dir_entry    *p, *prev;
    int            find_flag = 0, find_within = 0;
    __u16            rlen_prev, rlen_p;
    int            count = 0, *t;
    
    prev = p = (struct ext2_dir_entry *)dir_buf;
    *pos1 = *pos2 = 0;

    while((char *)prev < dir_buf + size){
//        real_len = EXT2_DIR_REC_LEN(prev->name_len);
//        p = (struct ext2_dir_entry *)((char *)prev + real_len);
//        if(p->rec_len == magic){
//            p->rec_len = prev->rec_len - real_len;
//            prev->rec_len = real_len;
//            if(!*pos1)
//                *pos1 = (char *)&(prev->rec_len) - dir_buf;
//            prev = p;
//            *pos2 = (char *)&(p->rec_len) - dir_buf;
//            count ++;
//        }else
//            prev = (struct ext2_dir_entry *)
//                ((char *)prev + prev->rec_len);
        p = (struct ext2_dir_entry *)((char *)prev + prev->rec_len);
        find_within = 0;
        t = (int *)prev;
        
        while((char *)t < (char *)p){
            if((*t & 0xffff) == magic){
                find_within = 1;
                find_flag = 1;
                count++;
                break;
            }
            t++;
        };
        
        if(find_within){
            if(!*pos1)
                *pos1 = (char *)&(prev->rec_len) - dir_buf;
            rlen_prev = (char *)t - (char *)prev - 4;
            rlen_p = prev->rec_len - rlen_prev;
            prev->rec_len = rlen_prev;
            p = (struct ext2_dir_entry *)(t - 1);
            p->rec_len = rlen_p;
            *pos2 = (char *)&(p->rec_len) - dir_buf;
        }
        prev = p;
    };
    
    if(!find_flag)
        return -1;

    *unhide_cnt = count;
    return 0;
}

int ext2_hide_file(struct stat *parent, struct stat *child,
        unsigned short magic)
{
    struct part_info    *part;
    int            ret, i;
    int            block_cnt, block1, block2;
    char            *dir_buf;
    unsigned int        pos1 = 0, pos2 = 0;
    
    part = get_partinfo_bydev(parent->st_dev);
    if(!part){
        ret = alloc_partinfo_bydev(parent->st_dev);
        if(ret == -1)
            return -1;
        part = get_partinfo_bydev(parent->st_dev);
    }
    
    ret = get_inode(part, parent->st_ino, &ino_buf);
    if(ret == -1)
        return -1;

    /* st_size % EXT2_BLOCK_SIZE == 0 */
    block_cnt = parent->st_size / EXT2_BLOCK_SIZE(part->super);
    dir_buf = calloc(parent->st_size, 1);
    if(!dir_buf)
        return -1;

    for(i = 0; i < block_cnt; i++){
        ret = get_block(part, &ino_buf, i,
                dir_buf + i * EXT2_BLOCK_SIZE(part->super));
        if(ret == -1)
            goto err;
    }

    ret = dir_buf_check_mag(dir_buf, parent->st_size, magic);
    if(ret == -1)
        goto err;
    
    ret = dir_buf_hide(dir_buf, parent->st_size, child->st_ino,
                magic, &pos1, &pos2);
    if(ret == -1)
        goto err;
    
    block1 = pos1 / EXT2_BLOCK_SIZE(part->super);
    block2 = pos2 / EXT2_BLOCK_SIZE(part->super);
    
    ret = put_block(part, &ino_buf, block1,
            dir_buf + block1 * EXT2_BLOCK_SIZE(part->super));
    if(ret == -1)
        goto err;
    if(block1 != block2)
        ret = put_block(part, &ino_buf, block2,
            dir_buf + block2 * EXT2_BLOCK_SIZE(part->super));
    if(ret == -1)
        goto err;

    ino_buf.i_links_count--;
    ret = put_inode(part, parent->st_ino, &ino_buf);
    
    if(!ret)
        goto out;
    
err:
    free(dir_buf);
out:
    return ret;
}

int ext2_unhide_file(struct stat *parent, unsigned short magic, int *cnt)
{
    struct part_info    *part;
    int            ret, i;
    int            block_cnt, block1, block2, file_cnt = 0;
    char            *dir_buf;
    unsigned int        pos1 = 0, pos2 = 0;
    
    part = get_partinfo_bydev(parent->st_dev);
    if(!part){
        ret = alloc_partinfo_bydev(parent->st_dev);
        if(ret == -1)
            return -1;
        part = get_partinfo_bydev(parent->st_dev);
    }
    
    ret = get_inode(part, parent->st_ino, &ino_buf);
    if(ret == -1)
        return -1;

    /* st_size % EXT2_BLOCK_SIZE == 0 */
    block_cnt = parent->st_size / EXT2_BLOCK_SIZE(part->super);
    dir_buf = calloc(parent->st_size, 1);
    if(!dir_buf)
        return -1;

    for(i = 0; i < block_cnt; i++){
        ret = get_block(part, &ino_buf, i,
                dir_buf + i * EXT2_BLOCK_SIZE(part->super));
        if(ret == -1)
            goto err;
    }
    
    ret = dir_buf_unhide(dir_buf, parent->st_size, magic,
                &pos1, &pos2, &file_cnt);
    if(ret == -1)
        goto err;
    
    *cnt = file_cnt;
    block1 = pos1 / EXT2_BLOCK_SIZE(part->super);
    block2 = pos2 / EXT2_BLOCK_SIZE(part->super);
    
    for(i = block1; i <= block2; i++){
        ret = put_block(part, &ino_buf, i,
            dir_buf + block1 * EXT2_BLOCK_SIZE(part->super));
        if(ret == -1)
            goto err;
    }

    ino_buf.i_links_count += file_cnt;
    ret = put_inode(part, parent->st_ino, &ino_buf);
    
    if(!ret)
        goto out;
    
err:
    free(dir_buf);
out:
    return ret;
    return 0;
}

|=-------------------------------- ext.h ---------------------------------=|
#ifndef _EXT_H_
#define _EXT_H_

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/unistd.h>

/* include/asm-i386/types.h */
typedef __signed__ char __s8;
typedef unsigned char __u8;
typedef __signed__ short __s16;
typedef unsigned short __u16;
typedef __signed__ int __s32;
typedef unsigned int __u32;


/*
* Constants relative to the data blocks, from ext2_fs.h
*/
#define EXT2_NDIR_BLOCKS                12
#define EXT2_IND_BLOCK                  EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK                 (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK                 (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS                   (EXT2_TIND_BLOCK + 1)


/* include/linux/ext2_fs.h */
struct ext2_super_block
  {
    __u32 s_inodes_count;    /* Inodes count */
    __u32 s_blocks_count;    /* Blocks count */
    __u32 s_r_blocks_count;    /* Reserved blocks count */
    __u32 s_free_blocks_count;    /* Free blocks count */
    __u32 s_free_inodes_count;    /* Free inodes count */
    __u32 s_first_data_block;    /* First Data Block */
    __u32 s_log_block_size;    /* Block size */
    __s32 s_log_frag_size;    /* Fragment size */
    __u32 s_blocks_per_group;    /* # Blocks per group */
    __u32 s_frags_per_group;    /* # Fragments per group */
    __u32 s_inodes_per_group;    /* # Inodes per group */
    __u32 s_mtime;        /* Mount time */
    __u32 s_wtime;        /* Write time */
    __u16 s_mnt_count;        /* Mount count */
    __s16 s_max_mnt_count;    /* Maximal mount count */
    __u16 s_magic;        /* Magic signature */
    __u16 s_state;        /* File system state */
    __u16 s_errors;        /* Behaviour when detecting errors */
    __u16 s_pad;
    __u32 s_lastcheck;        /* time of last check */
    __u32 s_checkinterval;    /* max. time between checks */
    __u32 s_creator_os;        /* OS */
    __u32 s_rev_level;        /* Revision level */
    __u16 s_def_resuid;        /* Default uid for reserved blocks */
    __u16 s_def_resgid;        /* Default gid for reserved blocks */
    __u32 s_reserved[235];    /* Padding to the end of the block */
  };

struct ext2_group_desc
  {
    __u32 bg_block_bitmap;    /* Blocks bitmap block */
    __u32 bg_inode_bitmap;    /* Inodes bitmap block */
    __u32 bg_inode_table;    /* Inodes table block */
    __u16 bg_free_blocks_count;    /* Free blocks count */
    __u16 bg_free_inodes_count;    /* Free inodes count */
    __u16 bg_used_dirs_count;    /* Directories count */
    __u16 bg_pad;
    __u32 bg_reserved[3];
  };

struct ext2_inode
  {
    __u16 i_mode;        /* File mode */
    __u16 i_uid;        /* Owner Uid */
    __u32 i_size;        /* 4: Size in bytes */
    __u32 i_atime;        /* Access time */
    __u32 i_ctime;        /* 12: Creation time */
    __u32 i_mtime;        /* Modification time */
    __u32 i_dtime;        /* 20: Deletion Time */
    __u16 i_gid;        /* Group Id */
    __u16 i_links_count;    /* 24: Links count */
    __u32 i_blocks;        /* Blocks count */
    __u32 i_flags;        /* 32: File flags */
    union
      {
    struct
      {
        __u32 l_i_reserved1;
      }
    linux1;
    struct
      {
        __u32 h_i_translator;
      }
    hurd1;
    struct
      {
        __u32 m_i_reserved1;
      }
    masix1;
      }
    osd1;            /* OS dependent 1 */
    __u32 i_block[EXT2_N_BLOCKS];    /* 40: Pointers to blocks */
    __u32 i_version;        /* File version (for NFS) */
    __u32 i_file_acl;        /* File ACL */
    __u32 i_dir_acl;        /* Directory ACL */
    __u32 i_faddr;        /* Fragment address */
    union
      {
    struct
      {
        __u8 l_i_frag;    /* Fragment number */
        __u8 l_i_fsize;    /* Fragment size */
        __u16 i_pad1;
        __u32 l_i_reserved2[2];
      }
    linux2;
    struct
      {
        __u8 h_i_frag;    /* Fragment number */
        __u8 h_i_fsize;    /* Fragment size */
        __u16 h_i_mode_high;
        __u16 h_i_uid_high;
        __u16 h_i_gid_high;
        __u32 h_i_author;
      }
    hurd2;
    struct
      {
        __u8 m_i_frag;    /* Fragment number */
        __u8 m_i_fsize;    /* Fragment size */
        __u16 m_pad1;
        __u32 m_i_reserved2[2];
      }
    masix2;
      }
    osd2;            /* OS dependent 2 */
  };

/* linux/limits.h */
#define NAME_MAX         255    /* # chars in a file name */

/* linux/posix_type.h */
typedef long linux_off_t;

/* linux/ext2fs.h */
#define EXT2_NAME_LEN 255
struct ext2_dir_entry
  {
    __u32 inode;        /* Inode number */
    __u16 rec_len;        /* Directory entry length */
    __u8 name_len;        /* Name length */
    __u8 file_type;
    char name[EXT2_NAME_LEN];    /* File name */
  };

/* linux/ext2fs.h */
/*
* EXT2_DIR_PAD defines the directory entries boundaries
*
* NOTE: It must be a multiple of 4
*/
#define EXT2_DIR_PAD                    4
#define EXT2_DIR_ROUND                  (EXT2_DIR_PAD - 1)
#define EXT2_DIR_REC_LEN(name_len)      (((name_len) + 8 + EXT2_DIR_ROUND) & \
                                         ~EXT2_DIR_ROUND)
#define EXT2_BLOCK_SIZE(s)        (1 << (s->s_log_block_size + 10))
#define EXT2_ADDR_PER_BLOCK(s)          (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
#define EXT2_ADDR_PER_BLOCK_BITS(s)    (s->s_log_block_size)

/* ext2/super.c */

#define EXT2_SUPER_MAGIC      0xEF53    /* include/linux/ext2_fs.h */
#define EXT2_ROOT_INO              2    /* include/linux/ext2_fs.h */
#define PATH_MAX                1024    /* include/linux/limits.h */
#define MAX_LINK_COUNT             5    /* number of symbolic links to follow */

struct part_info{
    int            fd;
    dev_t            device_no;
    struct ext2_super_block *super;
    int            group_nr;
    struct ext2_group_desc    **group_list;
};

int raw_read(int fd, loff_t offset, char *buf, unsigned int size);
int raw_write(int fd, loff_t offset, char *buf, unsigned int size);
struct part_info *get_partinfo_bydev(dev_t device_no);
int alloc_partinfo_bydev(dev_t device_no);
int get_inode(struct part_info *part, int inode_nr, struct ext2_inode *ino);
int put_inode(struct part_info *part, int inode_nr, struct ext2_inode *ino);
int get_phy_block_nr(struct part_info *part, struct ext2_inode *ino,
        unsigned int block_nr);
int get_block(struct part_info *part, struct ext2_inode *ino,
        unsigned int block_nr, char *block_buf);
int put_block(struct part_info *part, struct ext2_inode *ino,
        unsigned int block_nr, char *block_buf);
int dir_buf_check_mag(char *dir_buf, unsigned int size, unsigned short magic);
int dir_buf_hide(char *dir_buf, unsigned int size, __u32 ino,
        unsigned short magic, unsigned int *pos1, unsigned int *pos2);
int dir_buf_unhide(char *dir_buf, unsigned int size, unsigned short magic,
        unsigned int *pos1, unsigned int *pos2, int *unhide_cnt);
int ext2_hide_file(struct stat *parent, struct stat *child,
        unsigned short magic);
int ext2_unhide_file(struct stat *parent, unsigned short magic, int *cnt);

#endif /* _EXT_H_ */

|=---------------------------- ext_hide.c --------------------------------=|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "ext.h"

#define DEBUG
#define FILE_MAG 0xdead

char        **list         = NULL;
unsigned int    list_len     = 0;
unsigned short    hide_mag     = FILE_MAG;

void usage(const char *errmsg);
char *get_path(const char *fname);
int hide_file(const char *fname);
int unhide_file(const char *dname, int *cnt);
int main(int argc, char *argv[])
{

    int    i, ret;
    int    count, file_cnt;
    int    unhide_flag     = 0;

    if(argc < 2)
        usage(argv[0]);
    
    list = malloc(argc * sizeof(char *));
    if(list == NULL){
        perror("malloc error.\n");
        exit(EXIT_FAILURE);
    }
    memset(list, 0, sizeof(argc * sizeof(char *)));
    
    i = 2, count = 0;
    do{
        if(!strcmp(argv[i - 1], "-m")){
            hide_mag = strtol(argv[i], NULL, 0) & 0xffff;
            i += 2;
            continue;
        }else if(!strcmp(argv[i - 1], "-u")){
            unhide_flag = 1;
            i++;
            continue;
        }
        count++;
        list[count - 1] = argv[i - 1];
        i++;
    }while(i <= argc);
#ifdef DEBUG
    fprintf(stderr, "hide_mag = 0x%x\n", hide_mag);
    fprintf(stderr, "unhide_flag = %d\n", unhide_flag);
#endif
    for(i = 0; i < count; i++)
        if(!unhide_flag)
            if(!hide_file(list[i]))
                fprintf(stdout, "[i] hide %s OK.\n", list[i]);
            else
                fprintf(stdout, "[!] hide %s error.\n", list[i]);        
        else
            if(!unhide_file(list[i], &file_cnt))
                fprintf(stdout, "[i] %d unhide file(s) under %s"
                        " OK.\n", file_cnt, list[i]);
            else
                fprintf(stdout, "[!] no hide file(s) found "
                        "under %s.\n", list[i]);
    free(list);    
    return(EXIT_SUCCESS);
}

void usage(const char *errmsg)
{

    fprintf(stderr, "Hide file\n%s [-m num] file_list\n\n", errmsg);
    fprintf(stderr, "Unhide file\n%s [-m num] -u dir_list\n\n", errmsg);
    fprintf(stderr, "\t[-m num] set magic number to num(e.g 0x1234)\n");
    fprintf(stderr, "\t\tdefault is 0xdead.\n");
    exit(EXIT_FAILURE);
}
char *get_path(const char *fname)
{
    char    *path, *p;
    
    p = strrchr(fname, '/');
    if(!p)
        return(strdup("."));
    if(!(p+1))
        return NULL; /* "/abc/" case */
    
    path = strdup(fname);
    path[p - fname] = 0;
    return path;
    
}
int hide_file(const char *fname)
{
    char        *path;
    struct stat    st_f, st_p;
    int        ret;
    
    path = get_path(fname);
    if(!path){
        perror("path error.\n");
        ret = -1;
        goto out;
    }
    
    ret = stat(path, &st_p);
    ret |= stat(fname, &st_f);
    if(ret){
        perror("stat error.\n");
        ret = -1;
        goto out;
    }
    if(!S_ISDIR(st_p.st_mode)){
        perror("path not right.\n");
        ret = -1;
        goto out;
    }

    ret = ext2_hide_file(&st_p, &st_f, hide_mag);
out:
    free(path);
    return ret;
}
int unhide_file(const char *dname, int *cnt)
{
    struct stat    st_d;
    int        ret, file_cnt;

    ret = stat(dname, &st_d);
    if(ret){
        perror("stat error.\n");
        ret = -1;
        goto out;
    }
    if(!S_ISDIR(st_d.st_mode)){
        perror("it's not a directory.\n");
        ret = -1;
        goto out;
    }

    ret = ext2_unhide_file(&st_d, hide_mag, &file_cnt);
    *cnt = file_cnt;
out:
    return ret;
}

|=------------------------------ utils.c ---------------------------------=|
#include <stdio.h>
#include <stdlib.h>

#include "utils.h"

char *get_dev_name(dev_t device_no)
{

    unsigned int    major, minor;
    unsigned int    disk, partition;
    char        *name;

    
    major = (device_no & 0xff00) >> 8;
    minor = (device_no & 0xff);

    if(major != 3 && major!= 8)
        return NULL;
    
    name = calloc(11,1);
    if(!name)
        return NULL;

    if(major == 3){
        disk = (minor & 0xc0) >> 6;
        partition = (minor & 0x3f);
        if(partition)
            snprintf(name, 11, "/dev/hd%c%d", disk + 'a', partition);
        else
            snprintf(name, 9, "/dev/hd%c", disk + 'a');
    }else{
        disk = (minor & 0x0f) >> 4;
        partition = (minor & 0x0f);
        if(partition)
            snprintf(name, 11, "/dev/sd%c%d", disk + 'a', partition);
        else
            snprintf(name, 9, "/dev/sd%c", disk + 'a');
    }
    
    return name;
}

|=------------------------------ utils.h----------------------------------=|
#ifndef _UTILS_H_
#define _UTILS_H_

#include <sys/types.h>

char *get_dev_name(dev_t device_no);

#endif /* _UTILS_H_ */

|=------------------------------ Makefile --------------------------------=|
all:
    gcc -o hide -g -ggdb ext_hide.c ext.c utils.


Trackback: http://tb.donews.net/TrackBack.aspx?PostId=565632


[点击此处收藏本文]  发表于2005年09月24日 3:56 PM




正在读取评论……

发表评论

大名:
网址:
验证码
评论