一、实验预期目的及实验环境
本实验意实现FAT32结构下的U盘隐写不被U盘文件系统读出,主要考察FAT32简单的结构认识和简单的代码能力。
为了方便实现,目标机环境设为Winxp sp2,附带Winhex和VS2010工具包。
二、基础知识
FAT32整体文件结构如下
本实验主要需要隐写文件至Upan处,方法采用修改FAT表、0号1号磁盘信息实现隐写。
0号磁盘结构中需要注意如下
偏移0BH:扇区字节数 00 02 即0X0200,512字节
偏移0DH:每簇扇区数 08即每簇包括8个扇区
偏移0EH:保留扇区数 24 00即保留36个扇区
偏移10H:FAT表份数 02即两个FAT表
偏移24H:FAT表占用扇区数 EE 1D 00 00即FAT表占7662个扇区
1号磁盘结构中需要注意如下
偏移0x1E8:空闲簇数 4个字节
偏移0x1Ec:下一可用簇号 4个字节
注意这里存储的都是小端序!
FAT32结构如下
FAT表项每一项占4字节,表项的地址编号对应相应的簇序号。
表项的内容如下:
若为0,该簇未被分配;
若为0xFFFFFF7,坏簇,内有坏扇区;
若为0x0FFFFFF,该簇为文件结束簇;
其它值,为对应文件的下一簇号
我们这里处理用0xFFFFFF7伪造坏簇。一般情况下FAT表会有多个(一般是2个,所以还是需要把每个表都改掉)
注意簇位置对应的公式如下!
保留区*扇区大小+(第一个簇标号-2)*每簇大小*512+FAT表个数*扇区大小*FAT表大小
为什么中间有个减2呢?原因是FAT中的0、1号簇对应的地方用来存放其他的内容了,所以对应的没有0、1号簇,最先的簇从2号开始。’
三、实现思路
这里使用的方法很简单,甚至很幼稚
1.U盘的检测用map数组,最开始标记,动态判定一定数量的的磁盘有无变化来检测
2.U盘中需要存放不连续的簇的位置,我直接写到了U盘的空白区0x800位置,其实如果藏到FAT表中的隐蔽性会更小,受到的大小限制也更小了(因为前面的空白区毕竟有限)
3.只是单纯为了验证效果,写入U盘,和读取U盘文件运行都是用的文件的形式,文件用的固定地址
四、代码
// Upan_killer.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "iostream"
#include "windows.h"
#include "DBT.H"
#include "fstream"
#include "map"
#include "string"
using namespace std;
#define A TEXT("\\\\.\\A:")
#define B TEXT("\\\\.\\B:")
#define C TEXT("\\\\.\\C:")
#define D TEXT("\\\\.\\D:")
#define E TEXT("\\\\.\\E:")
#define F TEXT("\\\\.\\F:")
#define G TEXT("\\\\.\\G:")
#define H TEXT("\\\\.\\H:")
#define I TEXT("\\\\.\\I:")
#define J TEXT("\\\\.\\J:")
char U[3]={
0}; //U盘的盘符
int LeftArea,FatSize; //分别是FAT区的保留区大小和FAT表的大小
int FirstCluster; //可以利用的第一个簇号码
int SectorLeft; //剩余可利用的空闲簇
int ClusterSize; //簇的大小
int ClusterNeed; //写入的exe文件所需要的簇数
map<string,int>mmp;
//判断文件是否被建立
int FileExits(char *FileName)
{
fstream _file;
_file.open(FileName,ios::in);
if(!_file) return 0;
else return 1;
}
//参数分别是文件句柄、数据缓冲区、准备文件读取的字节数和读取位置的偏移量
void ReadFileWithSize(HANDLE h,unsigned char* Buffer,DWORD dwBytestoRead,DWORD offset)
{
DWORD dwBytesRead,dwBytesToRead;
OVERLAPPED over = { 0 }; //计算读取的偏移量
over.Offset = offset;
dwBytesToRead = dwBytestoRead; // 要读取的字节数
dwBytesRead = 0; // 实际读取到的字节数
do{ //循环写文件,确保完整的文件被写入
ReadFile(h,Buffer,dwBytesToRead,&dwBytesRead,&over);
dwBytesToRead -= dwBytesRead;
Buffer += dwBytesRead;
} while (dwBytesToRead > 0);
}
//参数分别是文件句柄、数据缓冲区、准备文件读取的字节数和读取位置的偏移量
void WriteFileWithSize(HANDLE h,unsigned char* Buffer,DWORD dwBytesToWrite,DWORD offset)
{
DWORD dwBytesWrite;
OVERLAPPED over = { 0 }; //计算读取的偏移量
over.Offset = offset;
dwBytesToWrite = dwBytesToWrite; // 要写入的字节数
dwBytesWrite = 0; // 实际读取到的字节数
do{ //循环写文件,确保完整的文件被写入
WriteFile(h,Buffer,dwBytesToWrite,&dwBytesWrite,&over);
dwBytesToWrite -= dwBytesWrite;
Buffer += dwBytesWrite;
} while (dwBytesToWrite > 0);
}