设置密码隐密文件工具 v1.4
设计原理:在磁盘上创建一个文件夹,并通过修改此文件夹的访问控制列表(ACL)和文件夹属性从而“加密”此文件夹;之后在被“加密”的文件夹系统内部存储需要被隐藏的文件。访问文件夹系统内部需要密码,密码由程序用户设定。存储访问密码的文件同样被置于此文件夹系统内部,且被设定为系统及隐藏文件属性。
隐藏原理:设定访问控制列表(ACL)和文件属性。
设计缺陷:可在Windows资源管理器(explorer.exe)中通过手动键入路径,从而直接访问到被隐藏的文件。此外,深谙Windows文件系统以及Windows命令行的你可在5分钟内破解“加密”。
设计实现:基于Windows API(文件系统API及控制台系统API)。
/*设置密码隐密文件工具 v1.4
**By obertys 2020/08/13
**使用说明:
** 定义keyMAXLen宏为设置密码最大允许的长度
** 定义caclsPATH宏为最外层的文件夹绝对路径
** 定义targetPATH宏为内层文件夹的绝对路径(是caclsPATH的子路径)
** 定义dataFilePATH常量作为存贮密码的数据文件绝对路径(位于targetPATH内部,含文件名)
** 定义pragramTitle常量作为控制台显示标题
**注意事项:
** 除非出于研究程序工作原理的目的,否则勿更改文件夹系统的属性
** 此程序仅供个人学习使用;勿将此程序作为专业的文件加密工具
** 如磁盘根目录下有与caclsPATH同名的文件夹存在,则只需修改caclsPATH即可;如无必要,勿改动targetPATH或dataFilePATH的路径深度
** 如忘记密码或有其它问题,请联系程序作者或自己动手
**编译支持:
** Windows平台下的C编译器
**运行支持:
** Windows操作系统
*/
#include<conio.h>
#include<stdio.h>
#include<string.h>
#include<windows.h>
#define keyMAXLen 16 //密码最大长度
#define caclsPATH "C:\\data" //隐密文件夹一级路径
#define targetPATH caclsPATH"\\crtys" //存放隐密文件的目录(必须是caclsPATH的子目录)
const char* dataFilePATH=targetPATH"\\data.at"; //存贮密码的文件路径(必须是targetPATH的子路径)
const char* pragramTitle="设置密码隐密文件工具 v1.4";
#if (keyMAXLen<=0)
#error 密码最大长度无效
#endif // keyMAXLen
DWORD WINAPI Th(LPVOID pParam) //线程函数
{
getch(); //等待任意键输入
*(BOOL*)pParam=TRUE; //接受到输入后将函数传参置为TRUE
return 0;
}
DWORD fileExist(const char* filePath) //检验文件filePath是否存在;若不存在返回0,若存在返回文件属性DWORD值
{
WIN32_FIND_DATA fd;
HANDLE hFind=FindFirstFile(filePath,&fd);
if(hFind==INVALID_HANDLE_VALUE) //查找文件失败
return 0;
else
{
FindClose(hFind);
return fd.dwFileAttributes;
}
}
size_t readInputKey(char* key) //从标准输入流读取密码,key为指向一个char[keyMAXLen+1]数组的char指针,返回读取到的密码实际长度
{
*key='\0';
size_t inputKeyLen=0; //密码实际长度
char c;
while((c=getch())!='\r') //敲下回车键输入终止
if(c=='\b') //敲下退格键
{
if(inputKeyLen>0)
{
key[--inputKeyLen]='\0'; //密码删除最后一位
printf("\b \b"); //清除一个字符'*'
}
}
else if(inputKeyLen<keyMAXLen)
{
key[inputKeyLen++]=c; //密码后缀当前输入字符
key[inputKeyLen]='\0'; //确保空结尾
putchar('*'); //打印一个字符'*'
}
putchar('\n');
return inputKeyLen;
}
int newKey(void) //设置新密码
{
/*输入密码*/
char inputKey_A[keyMAXLen+1],inputKey_B[keyMAXLen+1];
size_t len; //新密码长度
for(BOOL loop=TRUE; loop; )
{
printf("请设置新密码:");
readInputKey(inputKey_A);
printf("请再键入一次:");
len=readInputKey(inputKey_B);
if(strcmp(inputKey_A,inputKey_B)!=0)
puts("密码不一致!");
else if(len==0)
puts("密码不能为空!");
else
loop=FALSE; //当新密码合法时结束循环
}
/*打开数据文件*/
DWORD dataFileAttrib=fileExist(dataFilePATH);
if(dataFileAttrib!=0) //数据文件存在
{
if((dataFileAttrib&FILE_ATTRIBUTE_SYSTEM)==FILE_ATTRIBUTE_SYSTEM) //数据文件是系统属性
dataFileAttrib^=FILE_ATTRIBUTE_SYSTEM; //解除系统属性
if((dataFileAttrib&FILE_ATTRIBUTE_HIDDEN)==FILE_ATTRIBUTE_HIDDEN) //数据文件是隐藏属性
dataFileAttrib^=FILE_ATTRIBUTE_HIDDEN; //解除隐藏属性
SetFileAttributes(dataFilePATH,dataFileAttrib);
}
FILE* dataFile=fopen(dataFilePATH,"wb");
if(dataFile==NULL)
{
puts("无法打开数据文件\n未能设置新密码");
getch();
return 2;
}
/*写入数据文件*/
fwrite(inputKey_A,sizeof(char),len,dataFile); //向文件写入密码(仅含有效字符,不含'\0'或'\n')
fclose(dataFile);
if(dataFileAttrib==0) //数据文件是新创建的
dataFileAttrib=GetFileAttributes(dataFilePATH); //获取最新属性
dataFileAttrib|=FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM; //设置隐藏属性及系统属性
SetFileAttributes(dataFilePATH,dataFileAttrib);
puts("新密码已设置\n");
getch();
return 0;
}
int main(void)
{
/*初始化界面*/
system("cmd /c mode con cols=33 lines=8&color 0A"); //设置窗口大小及颜色
SetConsoleTitle(pragramTitle); //设置窗口标题
printf("欢迎使用%s\n",pragramTitle);
/*初始化文件目录*/
if(CreateDirectory(caclsPATH,NULL)) //尝试创造目录
{
if(!CreateDirectory(targetPATH,NULL)) //在新创建的caclsPATH下创建targetPATH
{
printf("路径错误:\"%s\"\n",targetPATH);
return 4;
}
DWORD attrib=GetFileAttributes(caclsPATH);
attrib|=FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM; //设置caclsPATH为隐藏属性及系统属性
SetFileAttributes(caclsPATH,attrib);
system("cmd /c echo Y|cacls "caclsPATH" /d everyone>nul"); //使用cacls对目录caclsPATH执行权限保护
HANDLE fileHAND=CreateFile(targetPATH"\\将需要被隐藏的文件或文件夹置于此目录下.txt",GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,NULL);
if(fileHAND!=INVALID_HANDLE_VALUE)
CloseHandle(fileHAND);
puts("已设置加密文件存放路径");
}
else
{
DWORD errorDWORD=GetLastError();
if(errorDWORD!=ERROR_ALREADY_EXISTS) //无法创造目录且目录并非已存在
{
printf("路径错误:\"%s\"\n",caclsPATH);
return 3;
}
if(CreateDirectory(targetPATH,NULL)) //尝试在已存在的caclsPATH下创建targetPATH
{
HANDLE fileHAND=CreateFile(targetPATH"\\将需要被隐藏的文件或文件夹置于此目录下.txt",GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,NULL);
if(fileHAND!=INVALID_HANDLE_VALUE)
CloseHandle(fileHAND);
puts("已恢复不存在的加密文件存放路径");
}
}
/*初始化文件*/
FILE* dataFile=fopen(dataFilePATH,"rb");
if(dataFile==NULL) //可能不存在数据文件
{
int res=newKey(); //尝试设置新密码
if(res!=0) //新密码设置失败
return res;
dataFile=fopen(dataFilePATH,"rb");
}
char fileKey[keyMAXLen+1]; //从文件读取的密码
memset(fileKey,0,sizeof(fileKey));
fread(fileKey,sizeof(char),keyMAXLen+1,dataFile);
fclose(dataFile);
/*输入密码*/
char inputKey[keyMAXLen+1]; //输入的密码
printf("密码:");
readInputKey(inputKey);
if(strcmp(fileKey,inputKey)==0) //密码正确
{
/*打开路径并等待输入*/
system("explorer "targetPATH); //打开目标路径
HANDLE outH=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO coordToGet;
GetConsoleScreenBufferInfo(outH,&coordToGet); //为了得到当前光标所在行数
COORD cursorPosition= {.X=0,.Y=coordToGet.dwCursorPosition.Y};
BOOL edit=FALSE; //判断是否需要修改密码
DWORD id;
HANDLE threadH=CreateThread(NULL,0,Th,&edit,0,&id); //开线程接受任意键输入
printf(" 秒钟后程序退出,按任意键修改密码");
for(char i='3'; i>='0'&&!edit; --i) //3秒钟倒计时
{
FillConsoleOutputCharacter(outH,i,1,cursorPosition,NULL); //刷新秒数显示
WaitForSingleObject(threadH,1000); //等待线程结束并同时等待1秒钟(不阻塞标准输入流,调用Sleep()会阻塞标准输入流)
}
if(edit) //线程已接受到任意键输入
{
COORD nextLine= {.X=0,.Y=cursorPosition.Y+1};
SetConsoleCursorPosition(outH,nextLine); //相当于putchar('\n');
CloseHandle(threadH);
return newKey();
}
else //线程仍处于接受输入状态
{
TerminateThread(threadH,id); //强制退出线程
CloseHandle(threadH);
return 0;
}
}
else
{
puts("密码错误!");
getch();
return 1;
}
}