2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C# メモリ上の数値の改ざんを防ぐ

Posted at

#成果物
C#のコンソールテスト用です
https://github.com/Oinary/SecureValue/blob/master/Program.cs

#1.やりたいこと
・改ざんされちゃマズい数値を守りたい!(ゲーム内のHPやお金など)
・ファイルだけではなくメモリ上の数値も保護したい。(今回はInt型とFloat型)
#2.何を使えば出来るか
XOR(排他的論理和)で解決。
https://qiita.com/kuuso1/items/778acaa7011d98a3ff3a

簡単に言うと2進数で2つの数を演算したとき
片方がTrue(1)ならTrue。      0 xor 1 → 1
両方がTrue又はFalse(0)ならFalse。  0 xor 0 → 0

これって同じ値で演算しなおすと元の値に戻るんです。
1010 xor 0110 → 1100
1100 xor 0110 → 1010

科学の力ってすげぇ(てきとう
#3.実装してみた
まずXORを使うのには、元の値とそれを合わせる値が必要。
C#でランダムな整数をもらってきます。(これはちゃんと保存しましょう)

Random.Next()
https://docs.microsoft.com/ja-jp/dotnet/api/system.random.next?view=netframework-4.8

あとは ^ 演算子で保存、復元すれば解決!

SecureInt.cs
private int Seed;
private int Value;
public int _Value
 {
    get
    {
        return Value ^ Seed;
    }
    set
    {
        Seed = new Random().Next()//ランダムなInt
        Value = value ^ Seed;//あとはお好きに
    }
}

##3-1.そうは上手くいかなかった
Int型は何も問題なく実装できましたが…。
Float型ってXORを直接は使えない。(馬鹿には分からなかった。

##3-2.じゃぁどう回避しようか
ヒントがここにあった…。
https://qiita.com/nia_tn1012/items/d26f0fc993895a09b30b

Floatで後ろ23ビットは仮数部と呼ばれる、我々が目にする数が格納されているところがあります。
例 2.345 → 2345 (小数点を無くした値になります)
その仮数部を隠せば良さげです。
※これはイメージです

Sample
float F = 1.55f;//得たfloat
var I = BitConverter.ToInt32(BitConverter.GetBytes(F),0);//これをbyte[]にして、Int型にさせる
var SignificandPart = I << 9;//左に9ビットシフトすると仮数部だけ残る
var SecureValue = SignificandPart ^ Seed;//あとは煮るなり焼くなり
//戻すときはこの逆で

で出来上がりなのですが、まぁ操作多いんですよ。
なんかこう、もっとサクッとFloatをIntにできないんですかね…。

・・・あったわ

その参照記事の下に別の方法が書かれています。
ポインタを扱うこと がポイント♪ で内部値をInt型で取得できます。

SecureFloat.cs
private int SecValue;
private int Seed;
public unsafe float _Value
{
   get
   {
       var result = SecValue ^ Seed;
       return BitConverter.ToSingle(BitConverter.GetBytes(result), 0);//戻すときは一度byte[]にしてからFloatに変換。
   }
   set
   {
      CalculatingSeed();
      int v = *((int*)&value);//&valueでvalueのポインタを取得、(int*)でInt型のポインタを格納、最初の*でそのポインタの値を指す。
      SecValue = v ^ Seed;//あとはお好きに
   }
}

ポインタの使用は、本来はバグなど意図しない動作を防ぐために制限されています。
それを解除するため、unsafe を明示します。([VisualStudioの設定もお忘れずに][link-1])
[link-1]:https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/compiler-options/unsafe-compiler-option

これで守りたい値の保護方法が実装できそうです。

#4.さいごに
不正の種類はいくつもあるので、それ相応の対応をしなければなりません。
が、あの手この手で不正してくるはずです。
実装するコストと見合っているかどうかも検討しないと、クオリティや製作時間に響いちゃいます。

僕みたいな初級の個人ゲームクリエイターならお遊び程度でいいんじゃないでしょうかね。

では初記事をご覧いただきありがとうございました。

※ってか一番大事なメモリエディタの画像なくね?

2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?