設定密碼隱密檔案工具 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;
}
}