作者: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