使 .txt 文件不可读/不可编辑

Make .txt file unreadable / uneditable

我有一个程序可以保存一个带有高分的小 .txt 文件:

 // Create a file to write to. 
string createHighscore = _higscore + Environment.NewLine;
File.WriteAllText(path, createText);

// Open the file to read from. 
string createHighscore = File.ReadAllText(path);

问题是用户可以使用文本编辑器尽可能简单地编辑文件。所以我想使文件不可读/不可编辑或加密

我的想法是可以将数据保存在资源文件中,但是我可以在资源文件中写入吗? 或者另存为.dll,encrypt/decrypt或者找一个MD5-sum/hash.

可能您最好的选择是使用标准 NT 安全保护整个文件并以编程方式更改访问控制列表以保护整个文件不被不需要的用户编辑(当然,冒充您自己的应用程序的用户除外)。

密码学在这里无能为力,因为文件仍然可以使用常规文本编辑器(例如,notepad)进行编辑,最终用户只需添加一个额外的字符(或删除一个字符)就可以破坏文件也是)。

有一种不涉及编程工作的替代方法...

告诉您的用户,一旦他们手动编辑了整个文本文件,他们就失去了您的支持。归根结底,如果您存储此数据是因为您的应用程序需要它。损坏它或执行手动编辑它的危险任务可能会使您的应用程序产生错误。

另一种涉及编程工作的替代方法...

无论何时从您的应用程序更改文件,您都可以计算 MD5 或 SHA 哈希值并存储在一个单独的文件中,一旦您想再次读取或写入它,您将检查整个文件在再次写入之前生成相同的哈希值。

这样,用户仍然可以手动编辑您的文件,但您会知道用户何时做出了这种意外行为(除非用户也手动计算哈希每当文件更改时...)。

简单的解决方法:
为了减轻 hackish 用户更改分数的能力,我猜你可以把它写成二进制文件。
另一个解决方案:
将数据写入 SQLite 数据库?

您不能在资源中写入,this answer

中有更多信息

The reason that you can't change a resource string at runtime, is because the resource is compiled into your executable. If you reverse engineer the compiled *.exe or *.dll file, you can actually see your string in the code. Editing an already compiled executable file is never a good idea (unless you're trying to hack it), but when you try to do it from the executables code, it just plain impossible, as the file is locked during execution.

  • 您可以添加 只读隐藏 属性到您的文件使用 File.SetAttributes,但用户仍然可以删除属性 来自 windows 并编辑文件。

一个例子:

File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
  • 我建议的另一种方法是将数据保存在一个文件中,其中包含一些 奇怪的扩展,以至于用户无法将其视为可编辑或 重要文件。类似 ghf.ytr 的东西(想不出东西 现在更奇怪了!)
  • 我还建议制作一个扩展名为 .dll 的文本文件,并将其保存在 windows 文件夹之一中,例如 system32。这样用户将很难找到分数信息的去向!

您可以将您的文件命名为不表明其中有分数 table 的名称(例如 YourApp.dat)并加密内容。

采纳答案here包含文本加解密代码

更新
我还建议使用一些 Guid 作为加密的密码。

不能在dll中保存数据,资源文件和txt文件都是可编辑的。听起来加密是您唯一的出路。您可以在将字符串保存到 txt 文件之前对其进行加密。看看这个线程: Encrypt and decrypt a string

您不能阻止用户修改文件。这是他们的电脑,所以他们可以做任何他们想做的事(这就是为什么整个 DRM 问题是……困难的)。

既然你说你正在使用该文件来保存高分,那么你有几个选择。请注意,如前所述,没有任何方法可以阻止真正坚定的攻击者篡改该值:由于您的应用程序在用户计算机上 运行 他可以简单地反编译它,看看您如何保护该值(获得访问过程中使用的任何秘密)并采取相应行动。但是如果你愿意反编译一个应用程序,找出所使用的保护方案并想出一个 script/patch 来绕过它只是为了改变一个只有你能看到的数字,好吧,去做吧?

混淆内容

这将阻止用户直接编辑文件,但不会在知道混淆算法后立即阻止他们。

var plaintext = Encoding.UTF8.GetBytes("Hello, world.");
var encodedtext = Convert.ToBase64String(plaintext);

将密文保存到文件中,读取文件时逆向进行

签署内容

这不会阻止用户编辑文件或查看其内容(但你不在乎,高分不是秘密)但你将能够检测到用户是否篡改了它。

var key = Encoding.UTF8.GetBytes("My secret key");
using (var algorithm = new HMACSHA512(key))
{
    var payload = Encoding.UTF8.GetBytes("Hello, world.");
    var binaryHash = algorithm.ComputeHash(payload);
    var stringHash = Convert.ToBase64String(binaryHash);
}

将有效载荷和哈希值都保存在文件中,然后在读取文件时检查保存的哈希值是否与新计算的哈希值匹配。您的密钥必须保密。

加密内容

利用 .NET 的加密库在保存内容之前对其进行加密,并在读取文件时对其进行解密。

请对以下示例持保留态度,并在实施之前花适当的时间了解所有内容(是的,您将出于微不足道的原因使用它,但将来您或其他人可能不是)。请特别注意如何生成 IV 和密钥。

// The initialization vector MUST be changed every time a plaintext is encrypted.
// The initialization vector MUST NOT be reused a second time.
// The initialization vector CAN be saved along the ciphertext.
// See https://en.wikipedia.org/wiki/Initialization_vector for more information.
var iv = Convert.FromBase64String("9iAwvNddQvAAfLSJb+JG1A==");

// The encryption key CAN be the same for every encryption.
// The encryption key MUST NOT be saved along the ciphertext.
var key = Convert.FromBase64String("UN8/gxM+6fGD7CdAGLhgnrF0S35qQ88p+Sr9k1tzKpM=");

using (var algorithm = new AesManaged())
{
    algorithm.IV = iv;
    algorithm.Key = key;

    byte[] ciphertext;

    using (var memoryStream = new MemoryStream())
    {
        using (var encryptor = algorithm.CreateEncryptor())
        {
            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
            {
                using (var streamWriter = new StreamWriter(cryptoStream))
                {
                    streamWriter.Write("MySuperSecretHighScore");
                }
            }
        }

        ciphertext = memoryStream.ToArray();
    }

    // Now you can serialize the ciphertext however you like.
    // Do remember to tag along the initialization vector,
    // otherwise you'll never be able to decrypt it.

    // In a real world implementation you should set algorithm.IV,
    // algorithm.Key and ciphertext, since this is an example we're
    // re-using the existing variables.
    using (var memoryStream = new MemoryStream(ciphertext))
    {
        using (var decryptor = algorithm.CreateDecryptor())
        {
            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            {
                using (var streamReader = new StreamReader(cryptoStream))
                {
                    // You have your "MySuperSecretHighScore" back.
                    var plaintext = streamReader.ReadToEnd();
                }
            }
        }
    }
}

您可以使用 CryptoStream 加密序列化和反序列化:

序列化文件:

  • 以写入模式创建和打开 FileStream
  • 创建 Cryptostream 并传递您的文件流
  • 将内容写入 Cryptostream(加密)

反序列化文件:

  • 以读取模式创建和打开 FileStream
  • 创建 Cryptostream 并传递您的文件流
  • 从 Cryptostream 读取(解密)

您可以在此处找到示例和更多信息:

msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.aspx

http://www.codeproject.com/Articles/6465/Using-CryptoStream-in-C

示例:

byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8 }; // Where to store these keys is the tricky part, 
byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
string path = @"C:\path\to.file";

DESCryptoServiceProvider des = new DESCryptoServiceProvider();

// Encryption and serialization
using (var fStream = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var cryptoStream = new CryptoStream(fStream , des.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
    BinaryFormatter serializer = new BinaryFormatter();

    // This is where you serialize your data
    serializer.Serialize(cryptoStream, yourData);
}



// Decryption
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var cryptoStream = new CryptoStream(fs, des.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
    BinaryFormatter serializer = new BinaryFormatter();

    // Deserialize your data from file
    yourDataType yourData = (yourDataType)serializer.Deserialize(cryptoStream);
}

由于您似乎在寻找相对较低的安全性,我实际上建议您使用校验和。一些伪代码:

string toWrite = score + "|" + md5(score+"myKey") + Environment.NewLine

如果分数是 100,这将变成

100|a6b6b0a8e56e42d8dac51a4812def434

为确保用户没有对文件进行调侃,您可以使用:

string[] split = readString().split("|");
if (split[1] != md5(split[0]+"myKey")){
     alert("No messing with the scores!");
}else{
     alert("Your score is "+split[0]);
}

当然,一旦有人知道了您的密钥,他们就可以随心所欲地处理这个问题,但我认为这超出了这个问题的范围。同样的风险适用于任何 encryption/decryption 机制。

正如下面评论中提到的,其中一个问题是,一旦有人(通过暴力破解)找到了您的密钥,他们就可以共享它,并且每个人都可以非常轻松地更改他们的文件。解决此问题的一种方法是向密钥添加特定于计算机的内容。例如,登录用户的名称,运行 通过 md5.

string toWrite = score + "|" + md5(score+"myKey"+md5(System.username /**or so**/)) + Environment.NewLine

这将防止密钥被 "simply shared"。

我还没有看到提到的是将高分存储在在线排行榜上。显然,这个解决方案需要更多的开发,但既然你在谈论游戏,你可能会利用第三方供应商,如 Steam、Origin、Uplay ......这具有排行榜的额外优势,而不仅仅是你的机器。

这是使文本文件不可编辑的代码。以同样的方式使用此技术使其不可读等

string pathfile = @"C:\Users\Public\Documents\Filepath.txt";

if (File.Exists(pathfile))
{
    File.Delete(pathfile);
}
if (!File.Exists(pathfile))
{
    using (FileStream fs = File.Create(pathfile))
    {
        Byte[] info = new UTF8Encoding(true).GetBytes("your text to be written to the file place here");

        FileSecurity fsec = File.GetAccessControl(pathfile);
        fsec.AddAccessRule(new FileSystemAccessRule("Everyone",
        FileSystemRights.WriteData, AccessControlType.Deny));
        File.SetAccessControl(pathfile, fsec);
    }
}