改竄されない変数の作り方

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

改竄されない変数の作り方

投稿記事 by Dixq (管理人) » 8年前

龍神録でお金の金額を改竄されるので
http://dixq.net/g/h_10.html
変数を常に排他的論理和でXOR暗号化していました。
しかしそれだけでは突破されてしまいました。

なので今回はint型の代わりにEncryptedIntで変数を保持するようにしました。
EncryptedIntクラスの中身はこのようになっています。
演算子オーバーロード部は省略していますが、普通のint型と同じように使えます。

ただし変数に値を代入するたびに変数のアドレスが変わりますので改竄を難しくしてあり
リンク先のような「スペシャルねこまんま57号」で値を絞っていくような方法には効果的だと思っています。

CODE:

#pragma once
class EncryptedInt
{
	int* value;
public:
	EncryptedInt();
	void set(int value);
	int  get();
};

CODE:

#include "EncryptedInt.h"
#include 

const static int KEY = 0x0123'4567'89AB'CDEF; //XOR暗号キー'

EncryptedInt::EncryptedInt()
{
	value = new int;
	*value = 0 ^ KEY;
}

EncryptedInt::~EncryptedInt()
{
	delete value;
}

void EncryptedInt::set(int v)
{
	int n = GetRand(3000); //適当にnew繰り返す分
	int** pool = new int*[n];

	int tmp = *value;

	for (int i = 0; i<n; i++) {
		pool[i] = new int;
	}

	delete value;
	value = new int;
	*value = v ^ KEY;

	for (int i = 0; i<n; i++) {
		delete pool[i];
	}
	delete[] pool;
}

int EncryptedInt::get()
{
	return (*value) ^ KEY;
}
思いつきではこれくらいですが、演算子オーバーロードして普通のint型として使えるようにしておけば
改竄されたくない値は全部これ使えばいいってことになりますね。
で、安全性を高めたいんですが、もっと解析が困難になる方法ありませんかね?
最後に編集したユーザー Dixq (管理人) on 2017年5月04日(木) 03:17 [ 編集 1 回目 ]

アバター
usao
記事: 1889
登録日時: 12年前

Re: 改竄されない変数の作り方

投稿記事 by usao » 8年前

素人丸出しな感想文ですが…

・EncryptedInt → より細かく, EncryptedByte * 4個 とか,EncryptedBit * 32個 とかに?
・誤り訂正(というか検出)符号的なやつ(*N個)を,同じようにEncryptedIntで別に持つ?

みたく,複数のアドレスを同時に改ざんしなきゃいけなくなるような方向はどうなんでしょう.

あと,3進数表記文字列で保有するとかしたら効果ありますか?

アバター
御津凪
管理人
記事: 200
登録日時: 14年前

Re: 改竄されない変数の作り方

投稿記事 by 御津凪 » 8年前

そもそもの改竄用ツールを起動されていればゲームが立ち上がらない(もしくは正しく動かせない)ようにする二段構えという手はどうでしょうか?
UE4の無料プラグインにSCUE4でそういった機能を持たせていて、ソースコードもあります(UE4のライセンスに従って参考にする必要がありますが)

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 改竄されない変数の作り方

投稿記事 by Dixq (管理人) » 8年前

> usaoさん

なるほど、EncryptedByte * 4個でEncryptedIntをなすって考え方いいですね。
より解析が難しくなりそうです。

誤り訂正はMD5的なハッシュ値を持っておけば良さそうですね。
龍神録1も同じ要領で改竄を発見し、改竄を発見したらセーブデータをロックする仕組みにしています。
ここまですればフリーゲームとしてはかなり頑張ってる方じゃないでしょうか。

ありがとうございます。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 改竄されない変数の作り方

投稿記事 by Dixq (管理人) » 8年前

> みつなぎさん

なるほど、別の角度からもガードするという発想もありですね。
Github見てみましたけどこれ解読するの骨が折れそう・・。
これって不正ツールじゃないツールがウォッチしているの誤認識したりしないんですかね?
例えばDxtoryやBandicam等で龍神録の画面を録画中だとか。

アバター
御津凪
管理人
記事: 200
登録日時: 14年前

Re: 改竄されない変数の作り方

投稿記事 by 御津凪 » 8年前

Dixq (管理人) さんが書きました:これって不正ツールじゃないツールがウォッチしているの誤認識したりしないんですかね?
例えばDxtoryやBandicam等で龍神録の画面を録画中だとか。
比較的簡素な判定をしているようなので(Process名・WindowClass名やウィンドウタイトル名など)、その辺りを真似すれば(ある程度と念をいれますが)対応できますね。

後はゲーム起動をワンクッション置く形で(起動用実行ファイルから)CreateProcess関数のセキュリティ記述子の引数を外部からのアクセスを禁止する設定をしてゲームを起動すればより強固に出来るかと思われます。(もちろん直接ゲーム本体を起動されないようにプログラムする必要も出てきますが)

YuO
記事: 947
登録日時: 14年前

Re: 改竄されない変数の作り方

投稿記事 by YuO » 8年前

ReadProcessMemory/WriteProcessMemoryを弾ければだいたいの問題が解決するので,SetSecurityInfoを呼び出して,プロセスオブジェクトのDACLで自身とBULITIN\Administrators (S-1-5-32-544) を拒否してみましたが……。

昇格されたプロセスからのアクセスを拒否できませんでした。
Administratorsからアクセスを拒否できないのは仕方が無い面もありますが,ちょっと腑に落ちません……。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 改竄されない変数の作り方

投稿記事 by Dixq (管理人) » 8年前

みつなぎさん

なるほど、その辺摸索してみます。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 改竄されない変数の作り方

投稿記事 by Dixq (管理人) » 8年前

YuOさん

おぉ、非常に参考になる情報ありがとうございます。
拒否できなくてもメモリが書き換えられたということは分かるのでしょうか?

YuO
記事: 947
登録日時: 14年前

Re: 改竄されない変数の作り方

投稿記事 by YuO » 8年前

Dixq (管理人) さんが書きました:拒否できなくてもメモリが書き換えられたということは分かるのでしょうか?
できないです。

ReadProcessMemoryする権限を他のプロセスが要求した場合にOpenProcessが失敗すればReadProcessMemoryできないよね,
というのがDACLによる拒否の基本的な発想なので,メモリが書き換えられた,などを検出しようとはしていません。
御津凪さんが
御津凪 さんが書きました:後はゲーム起動をワンクッション置く形で(起動用実行ファイルから)CreateProcess関数のセキュリティ記述子の引数を外部からのアクセスを禁止する設定をしてゲームを起動すればより強固に出来るかと思われます。(もちろん直接ゲーム本体を起動されないようにプログラムする必要も出てきますが)
と書かれている方法も,発想は同じだと思います。
起動時のセキュリティ記述子のDACLの設定で最初から拒否するか,起動後に自身のセキュリティ記述子のDACLを修正して途中から拒否するか,の違いになります。
オフトピック
起動時からDACLが拒否している場合にAdministratorsからアクセス不可かは試していないです。
理論上は同じなのですが……
メモリの書き込みの検出は……デバッガAPIを駆使すればできそうな気がするのですが,該当するものを見つけられませんでした。
# データブレークポイントを実現できればよい気がするのですが,方法がわからない……。