我有一个程序可以保存一个带有高分的小.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);
问题是用户可以使用texteditor尽可能简单地编辑文件。所以我想让文件不可读/不可编辑或加密。
我的想法是我可以将数据保存在资源文件中,但是我可以在资源文件中写入吗? 或者将其保存为.dll,加密/解密或查找MD5-sum / hash。
答案 0 :(得分:59)
您无法阻止用户修改文件。这是他们的计算机,所以他们可以做任何他们想做的事情(这就是为什么整个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();
}
}
}
}
}
答案 1 :(得分:12)
由于您似乎寻求相对较低的安全性,我实际上建议您选择校验和。一些伪代码:
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]);
}
当然,当有人知道你的钥匙时,他们可以随心所欲地解决这个问题,但我认为这超出了这个问题的范围。同样的风险适用于任何加密/解密机制。
下面的评论中提到的其中一个问题是,一旦有人找出你的密钥(通过暴力破解),他们就可以分享它,每个人都可以非常轻松地更改他们的文件。解决此问题的方法是在密钥中添加特定于计算机的内容。例如,登录用户的名称,通过md5。
string toWrite = score + "|" + md5(score+"myKey"+md5(System.username /**or so**/)) + Environment.NewLine
这样可以防止密钥被简单地共享"。
答案 2 :(得分:9)
您最好的选择是使用标准NT安全保护整个文件,并以编程方式更改访问控制列表,以保护整个文件不被不受欢迎的用户编辑(当然,除了假冒您自己的应用程序之外)。
加密技术在这里可以提供帮助,因为使用常规文本编辑器(例如notepad
)仍然可以编辑文件,最终用户可以只添加一个额外字符(或删除一个字符)来破坏文件太)。
告诉您的用户,一旦他们手动编辑了整个文本文件,他们就会失去您的支持。在一天结束时,如果您要存储此数据是因为您的应用程序需要这些数据。破坏它或执行手动编辑它的风险任务可能会使您的应用程序产生错误。
每当您从应用程序更改文件时,您都可以计算MD5或SHA哈希并存储在单独的文件中,一旦您想再次读取或写入,您就会检查整个文件在再次写入之前产生相同的哈希值。
这样,用户仍然可以手动编辑文件,但是您知道用户何时完成此意外行为(除非用户还在文件中手动计算哈希值)改变了......)。
答案 3 :(得分:6)
我还没有提到的一点是将高分存储在在线排行榜上。显然这个解决方案需要更多的开发,但是既然你在谈论游戏,你可能会使用像Steam,Origin,Uplay这样的第三方提供商......这有一个额外的优势,即排行榜不仅仅是为了你的机器。
答案 4 :(得分:5)
您无法在dll中保存数据,并且资源文件和txt文件都是可编辑的。听起来加密是唯一的方式。您可以在将字符串保存到txt文件之前对其进行加密。看一下这个帖子: Encrypt and decrypt a string
答案 5 :(得分:2)
简单解决方案:
为了减轻黑客用户改变分数的能力,我猜你可以把它写成二进制文件
另一种解决方案:
将数据写入SQLite DB?
答案 6 :(得分:2)
您可以使用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);
}
答案 7 :(得分:1)
您无法写入资源,this answer
中存在更多信息您无法在运行时更改资源字符串的原因是 因为资源已编译到您的可执行文件中。如果你逆转 设计编译的* .exe或* .dll文件,你实际上可以看到你的 代码中的字符串。编辑已编译的可执行文件是 永远不是一个好主意(除非你试图破解它),但当你尝试 从可执行代码中做到这一点,它完全不可能,因为 文件在执行期间被锁定。
一个例子:
File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
ghf.ytr
这样的东西(想不到什么
现在更奇怪了!).dll
扩展名的文本文件,并将其保存在system32
之类的Windows文件夹中。这样,用户将很难找到得分信息的去向!答案 8 :(得分:1)
您可以将文件命名为不建议其中包含分数表的内容(例如YourApp.dat)并加密内容。
接受的答案here包含加密和解密文本的代码。
<强>更新强>
我还建议使用一些#import "ViewController.h"
@interface ViewController ()
{
UILabel *viewLabel;
BOOL collapsed;
NSInteger collapsedSection;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initialization];
}
#pragma mark - Initialization
-(void)initialization
{
arrayForBool=[[NSMutableArray alloc]init];
sectionTitleArray=[[NSArray alloc]initWithObjects:
@"Apple",
@"Strawberry",
@"Grapes",
@"Orange",
@"Banana",
@"Papaya",
@"Guava",
@"pineapple",
nil];
for (int i=0; i<[sectionTitleArray count]; i++) {
[arrayForBool addObject:[NSNumber numberWithBool:NO]];
}
}
#pragma mark -
#pragma mark TableView DataSource and Delegate Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([[arrayForBool objectAtIndex:section] boolValue]) {
return section+2;
}
else
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellid=@"hello";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellid];
if (cell==nil) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellid];
}
BOOL manyCells = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
/********** If the section supposed to be closed *******************/
if(!manyCells)
{
cell.backgroundColor=[UIColor clearColor];
cell.textLabel.text=@"";
}
/********** If the section supposed to be Opened *******************/
else
{
cell.textLabel.text=[NSString stringWithFormat:@"%@ %d",[sectionTitleArray objectAtIndex:indexPath.section],indexPath.row+1];
cell.textLabel.font=[UIFont systemFontOfSize:15.0f];
cell.backgroundColor=[UIColor whiteColor];
cell.imageView.image=[UIImage imageNamed:@"point.png"];
cell.selectionStyle=UITableViewCellSelectionStyleNone ;
}
cell.textLabel.textColor=[UIColor blackColor];
/********** Add a custom Separator with cell *******************/
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(15, 40, _expandableTableView.frame.size.width-15, 1)];
separatorLineView.backgroundColor = [UIColor blackColor];
[cell.contentView addSubview:separatorLineView];
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sectionTitleArray count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
/*************** Close the section, once the data is selected ***********************************/
[arrayForBool replaceObjectAtIndex:indexPath.section withObject:[NSNumber numberWithBool:NO]];
[_expandableTableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[arrayForBool objectAtIndex:indexPath.section] boolValue]) {
return 40;
}
return 0;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 40;
}
#pragma mark - Creating View for TableView Section
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView *sectionView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, 280,40)];
sectionView.tag=section;
for (id subview in sectionView.subviews) {
if ([subview isKindOfClass:[UIImageView class]]) {
[subview removeFromSuperview];
} else if ([subview isKindOfClass:[UILabel class]]) {
[subview removeFromSuperview];
}
}
viewLabel=[[UILabel alloc]initWithFrame:CGRectMake(10, 0, _expandableTableView.frame.size.width-10, 40)];
viewLabel.font=[UIFont systemFontOfSize:15];
viewLabel.text=[NSString stringWithFormat:@"List of %@",[sectionTitleArray objectAtIndex:section]];
[sectionView addSubview:viewLabel];
if (collapsedSection == section) {
viewLabel.textColor = [UIColor redColor];
}else{
viewLabel.textColor = [UIColor orangeColor];
}
/********** Add a custom Separator with Section view *******************/
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(15, 40, _expandableTableView.frame.size.width-15, 1)];
separatorLineView.backgroundColor = [UIColor blackColor];
[sectionView addSubview:separatorLineView];
/********** Add UITapGestureRecognizer to SectionView **************/
UITapGestureRecognizer *headerTapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)];
[sectionView addGestureRecognizer:headerTapped];
return sectionView;
}
#pragma mark - Table header gesture tapped
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:gestureRecognizer.view.tag];
collapsedSection = gestureRecognizer.view.tag;
if (indexPath.row == 0) {
collapsed = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
for (int i=0; i<[sectionTitleArray count]; i++) {
if (indexPath.section==i) {
[arrayForBool replaceObjectAtIndex:i withObject:[NSNumber numberWithBool:!collapsed]];
}
}
[_expandableTableView reloadSections:[NSIndexSet indexSetWithIndex:gestureRecognizer.view.tag] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
@end
作为加密密码。
答案 9 :(得分:0)
以下是使文本文件无法编辑的代码。以同样的方式使用这种技术使其不可读等。
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);
}
}