設定密碼隱密檔案工具 v1.4

2020-08-13 10:42:05

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