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

本文关键字:编辑 txt 文件 | 更新日期: 2023-09-27 18:26:37

我有一个程序,它保存了一个小.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,加密/解密,或者查找MD5和/哈希。

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

您无法阻止用户修改文件。这是他们的电脑,所以他们可以随心所欲(这就是为什么整个DRM问题……很困难)。

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

混淆内容

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

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();
                }
            }
        }
    }
}

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

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

如果分数是100,这将成为

100|a6b0a6e42d8dac51a4812def434

为了确保用户不会对文件感到不满,您可以使用:

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

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

正如下面的评论中所提到的,其中一个问题是,一旦有人(通过暴力)找到了你的密钥,他们就可以共享它,每个人都可以很容易地更改他们的文件。解决这个问题的方法是在密钥中添加一些特定于计算机的内容。例如,通过md5运行的登录用户的名称。

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

这将防止密钥被"简单地共享"。

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

加密在这里没有帮助,因为使用常规文本编辑器(例如notepad)仍然可以编辑文件,并且最终用户只需添加一个额外的字符(或删除一个字符)就可以损坏文件。

还有一种不需要编程的替代方法

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

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

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

这样,用户仍然可以手动编辑您的文件,但您将知道用户何时完成了这种意外的行为(除非用户在文件更改时也手动计算哈希…)。

我还没有提到的是将高分存储在在线排行榜上。显然,这个解决方案需要更多的开发,但既然你谈论的是游戏,你可能会使用Steam、Origin、Uplay等第三方提供商。。。这还有一个额外的优势,那就是引线板不仅仅适用于您的机器。

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

您可以使用CryptoStream对其进行序列化和反序列化加密:

序列化文件:

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

反序列化文件:

  • 在读取模式下创建并打开FileStream
  • 创建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);
}

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

您可以将文件命名为不表明其中有记分表的内容(例如YourApp.dat),并对内容进行加密。

此处接受的答案包含用于加密和解密文本的代码。

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

您不能在资源中写入,更多信息存在于此答案中

不能在运行时更改资源字符串的原因是因为资源已编译为可执行文件。如果你反转对编译后的*.exe或*.dll文件进行工程设计,您实际上可以看到代码中的字符串。编辑已编译的可执行文件是从来都不是一个好主意(除非你试图破解它),但当你尝试从可执行程序代码中执行,这是完全不可能的,因为文件在执行过程中被锁定。

  • 您可以使用将只读隐藏属性添加到文件中File.SetAttributes,但用户仍然可以删除这些属性并编辑文件

一个例子:

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

下面是一个使文本文件不可编辑的代码。就像你使用这种技术使它不可读一样。

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);
    }
}