r205 vs r206
......
5555
== csharp 암호화 코드 ==
5656
아니 1만 8천자 추가된거 이거 맞는건가...
5757
휴대폰으로 AI 돌려서 코드 만드니 여기다가 저장해두는 것. 컴퓨터 열어서 유니티랑 VS 키기 귀찮아서 나중에 고치려고 여기가 적는게 맞습니다.
58
59
=== 예전 코드 ===
60
아래 신규 코드가 이상하면 이걸 쓰기위해
61
62
{{{#!syntax csharp
63
using System;
64
using System.IO;
65
using System.Security.Cryptography;
66
using System.Text;
67
using UnityEngine;
68
using System.Runtime.CompilerServices;
69
70
[Serializable]
71
public class PlayerData
72
{
73
public string playerName;
74
public int score;
75
}
76
77
public class SecureManager : MonoBehaviour
78
{
79
public static SecureManager Instance { get; private set; }
80
81
public bool EnableHardening = true;
82
public bool EnableAntiDebugChecks = true;
83
public bool EnableRootDetection = true;
84
public bool EnableDummyMix = false;
85
public bool EnableFileNameObfuscation = true;
86
public bool EnableRotation = true;
87
88
private const string Magic = "SJMP";
89
private const byte Version = 1;
90
private const int SaltLenDefault = 12;
91
private const int IvLen = 16;
92
private const int HmacLen = 32;
93
private const int KeyMaterialLen = 64;
94
private const int AesKeyLen = 32;
95
private const int HmacKeyLen = 32;
96
private const int Pbkdf2Iterations = 20000;
97
private readonly byte[] obfPartA = new byte[] { 0x4A, 0x5F, 0x33, 0x29, 0x11, 0x7C, 0x6D, 0x3E, 0x2D, 0x7A, 0x5B, 0x1C };
98
private readonly byte[] obfPartB = new byte[] { 0x91, 0x20, 0x55, 0x12, 0x44, 0x38, 0x77, 0x0A, 0x6F, 0x21, 0x9D, 0xEE };
99
private const byte Mask = 0xAA;
100
private string filePath;
101
private byte[] sessionNonce;
102
103
void Awake()
104
{
105
if (Instance != null && Instance != this)
106
{
107
Destroy(gameObject);
108
return;
109
}
110
Instance = this;
111
DontDestroyOnLoad(gameObject);
112
113
sessionNonce = LoadOrCreateSessionNonce();
114
filePath = Path.Combine(Application.persistentDataPath, ResolveFileName("playerData"));
115
Warmup();
116
}
117
118
private byte[] LoadOrCreateSessionNonce()
119
{
120
const string key = "SecureManager_SessionNonce_v1";
121
try
122
{
123
if (PlayerPrefs.HasKey(key))
124
{
125
string b64 = PlayerPrefs.GetString(key);
126
byte[] stored = Convert.FromBase64String(b64);
127
if (stored != null && stored.Length > 0) return stored;
128
}
129
}
130
catch { }
131
132
byte[] nonce = GenerateRandomBytes(16);
133
try
134
{
135
PlayerPrefs.SetString(key, Convert.ToBase64String(nonce));
136
PlayerPrefs.Save();
137
}
138
catch { }
139
return nonce;
140
}
141
142
private void Warmup()
143
{
144
try { _ = GetSecretBytes(); } catch { }
145
}
146
147
[MethodImpl(MethodImplOptions.NoInlining)]
148
private byte[] DumbMixA(byte[] input)
149
{
150
byte[] outb = new byte[input.Length];
151
for (int i = 0; i < input.Length; i++)
152
{
153
int v = input[i];
154
v = ((v << 3) | (v >> 5)) & 0xFF;
155
v ^= (i * 37) & 0xFF;
156
outb[i] = (byte)v;
157
}
158
return outb;
159
}
160
161
[MethodImpl(MethodImplOptions.NoInlining)]
162
private byte[] DumbMixB(byte[] input, int seed)
163
{
164
byte[] outb = new byte[input.Length];
165
for (int i = 0; i < input.Length; i++)
166
{
167
int v = input[i] ^ (seed >> (i % 8));
168
v = (v * 13 + (i * 7)) & 0xFF;
169
v = (v ^ 0x5A) & 0xFF;
170
outb[i] = (byte)v;
171
}
172
return outb;
173
}
174
175
[MethodImpl(MethodImplOptions.NoInlining)]
176
private byte[] DumbMixC(byte[] input, byte[] nonce)
177
{
178
byte[] outb = new byte[input.Length];
179
for (int i = 0; i < input.Length; i++)
180
{
181
int n = nonce[i % nonce.Length];
182
int v = input[i];
183
v = ((v + n) ^ (n >> (i % 4))) & 0xFF;
184
outb[i] = (byte)v;
185
}
186
return outb;
187
}
188
189
private byte[] GetSecretBytes()
190
{
191
byte[] k = new byte[obfPartA.Length + obfPartB.Length];
192
for (int i = 0; i < obfPartA.Length; i++) k[i] = (byte)(obfPartA[i] ^ Mask);
193
for (int i = 0; i < obfPartB.Length; i++) k[i + obfPartA.Length] = (byte)(obfPartB[i] ^ Mask);
194
195
if (!EnableHardening || !EnableDummyMix)
196
{
197
byte[] simple = new byte[k.Length];
198
Buffer.BlockCopy(k, 0, simple, 0, k.Length);
199
Array.Clear(k, 0, k.Length);
200
return simple;
201
}
202
203
byte[] s1 = DumbMixA(k);
204
205
// 시간 의존 제거, 디바이스 기반 결정적 시드
206
int deviceSeed = Application.identifier.GetHashCode() ^ SystemInfo.deviceUniqueIdentifier.GetHashCode();
207
208
byte[] s2 = DumbMixB(s1, deviceSeed);
209
byte[] s3 = DumbMixC(s2, sessionNonce);
210
for (int r = 0; r < 2; r++)
211
{
212
byte[] t = DumbMixA(s3);
213
byte[] u = DumbMixB(t, deviceSeed ^ r);
214
for (int i = 0; i < k.Length; i++) s3[i] = (byte)(s3[i] ^ u[i % u.Length]);
215
Array.Clear(t, 0, t.Length);
216
Array.Clear(u, 0, u.Length);
217
}
218
Array.Clear(s1, 0, s1.Length);
219
Array.Clear(s2, 0, s2.Length);
220
221
byte[] result = new byte[k.Length];
222
for (int i = 0; i < k.Length; i++) result[i] = (byte)(k[i] ^ s3[i % s3.Length]);
223
Array.Clear(k, 0, k.Length);
224
Array.Clear(s3, 0, s3.Length);
225
return result;
226
}
227
228
private (byte[] aesKey, byte[] hmacKey) DeriveKeysRotating(byte[] salt, byte[] rotationNonce)
229
{
230
byte[] secret = GetSecretBytes();
231
string secretStr = EnableHardening ? Convert.ToBase64String(secret) + Application.identifier : Encoding.UTF8.GetString(secret) + Application.identifier;
232
Array.Clear(secret, 0, secret.Length);
233
234
using (var kdf = new Rfc2898DeriveBytes(secretStr, salt, Pbkdf2Iterations, HashAlgorithmName.SHA256))
235
{
236
byte[] km = kdf.GetBytes(KeyMaterialLen);
237
if (EnableHardening && EnableRotation && rotationNonce != null)
238
{
239
for (int i = 0; i < km.Length; i++) km[i] ^= rotationNonce[i % rotationNonce.Length];
240
}
241
byte[] aesKey = new byte[AesKeyLen];
242
byte[] hmacKey = new byte[HmacKeyLen];
243
Buffer.BlockCopy(km, 0, aesKey, 0, AesKeyLen);
244
Buffer.BlockCopy(km, AesKeyLen, hmacKey, 0, HmacKeyLen);
245
Array.Clear(km, 0, km.Length);
246
return (aesKey, hmacKey);
247
}
248
}
249
250
public void Save<T>(T data)
251
{
252
if (EnableHardening && (EnableAntiDebugChecks || EnableRootDetection) && IsTamperedOrDebug()) return;
253
try
254
{
255
string json = JsonUtility.ToJson(data);
256
byte[] salt = GenerateRandomBytes(SaltLenDefault);
257
byte[] rotation = (EnableHardening && EnableRotation) ? GenerateRandomBytes(16) : new byte[16];
258
var keys = DeriveKeysRotating(salt, (EnableRotation ? rotation : null));
259
byte[] aesKey = keys.aesKey;
260
byte[] hmacKey = keys.hmacKey;
261
byte[] plain = Encoding.UTF8.GetBytes(json);
262
byte[] iv;
263
byte[] cipher;
264
using (Aes aes = Aes.Create())
265
{
266
aes.KeySize = 256;
267
aes.Mode = CipherMode.CBC;
268
aes.Padding = PaddingMode.PKCS7;
269
aes.Key = aesKey;
270
aes.GenerateIV();
271
iv = aes.IV;
272
using (var enc = aes.CreateEncryptor(aes.Key, iv))
273
cipher = enc.TransformFinalBlock(plain, 0, plain.Length);
274
}
275
byte[] ivCipher = new byte[iv.Length + cipher.Length];
276
Buffer.BlockCopy(iv, 0, ivCipher, 0, iv.Length);
277
Buffer.BlockCopy(cipher, 0, ivCipher, iv.Length, cipher.Length);
278
byte[] hmac;
279
using (var h = new HMACSHA256(hmacKey)) hmac = h.ComputeHash(ivCipher);
280
using (MemoryStream ms = new MemoryStream())
281
{
282
ms.Write(Encoding.ASCII.GetBytes(Magic), 0, 4);
283
ms.WriteByte(Version);
284
ms.WriteByte((byte)salt.Length);
285
ms.Write(salt, 0, salt.Length);
286
ms.WriteByte(EnableRotation ? (byte)rotation.Length : (byte)0);
287
if (EnableRotation) ms.Write(rotation, 0, rotation.Length);
288
ms.Write(iv, 0, iv.Length);
289
byte[] cipherLenBytes = BitConverter.GetBytes((Int32)cipher.Length);
290
if (!BitConverter.IsLittleEndian) Array.Reverse(cipherLenBytes);
291
ms.Write(cipherLenBytes, 0, 4);
292
ms.Write(cipher, 0, cipher.Length);
293
ms.Write(hmac, 0, hmac.Length);
294
string outBlob = Convert.ToBase64String(ms.ToArray());
295
File.WriteAllText(filePath, outBlob);
296
}
297
ClearBytes(aesKey);
298
ClearBytes(hmacKey);
299
ClearBytes(plain);
300
ClearBytes(iv);
301
ClearBytes(cipher);
302
ClearBytes(ivCipher);
303
ClearBytes(hmac);
304
ClearBytes(salt);
305
ClearBytes(rotation);
306
}
307
catch { }
308
}
309
310
public T Load<T>()
311
{
312
if (EnableHardening && (EnableAntiDebugChecks || EnableRootDetection) && IsTamperedOrDebug()) return default;
313
if (!File.Exists(filePath)) return default;
314
string blob = File.ReadAllText(filePath);
315
byte[] total;
316
try { total = Convert.FromBase64String(blob); } catch { return default; }
317
try
318
{
319
using (MemoryStream ms = new MemoryStream(total))
320
using (BinaryReader br = new BinaryReader(ms))
321
{
322
byte[] magic = br.ReadBytes(4);
323
if (Encoding.ASCII.GetString(magic) != Magic) return default;
324
byte ver = br.ReadByte();
325
if (ver != Version) return default;
326
int saltLen = br.ReadByte();
327
if (saltLen <= 0 || saltLen > 64) return default;
328
byte[] salt = br.ReadBytes(saltLen);
329
int rotationLen = br.ReadByte();
330
byte[] rotation = rotationLen > 0 ? br.ReadBytes(rotationLen) : new byte[16];
331
byte[] iv = br.ReadBytes(IvLen);
332
int cipherLen = br.ReadInt32();
333
if (!BitConverter.IsLittleEndian) cipherLen = System.Net.IPAddress.NetworkToHostOrder(cipherLen);
334
if (cipherLen <= 0 || cipherLen > total.Length) return default;
335
byte[] cipher = br.ReadBytes(cipherLen);
336
byte[] hmac = br.ReadBytes(HmacLen);
337
byte[] ivCipher = new byte[iv.Length + cipher.Length];
338
Buffer.BlockCopy(iv, 0, ivCipher, 0, iv.Length);
339
Buffer.BlockCopy(cipher, 0, ivCipher, iv.Length, cipher.Length);
340
var keys = DeriveKeysRotating(salt, (EnableRotation ? rotation : null));
341
byte[] aesKey = keys.aesKey;
342
byte[] hmacKey = keys.hmacKey;
343
byte[] expected;
344
using (var h = new HMACSHA256(hmacKey)) expected = h.ComputeHash(ivCipher);
345
bool ok = CryptographicOperations.FixedTimeEquals(expected, hmac);
346
if (!ok)
347
{
348
ClearBytes(aesKey);
349
ClearBytes(hmacKey);
350
ClearBytes(expected);
351
return default;
352
}
353
byte[] plain;
354
using (Aes aes = Aes.Create())
355
{
356
aes.KeySize = 256;
357
aes.Mode = CipherMode.CBC;
358
aes.Padding = PaddingMode.PKCS7;
359
aes.Key = aesKey;
360
aes.IV = iv;
361
using (var dec = aes.CreateDecryptor(aes.Key, aes.IV))
362
plain = dec.TransformFinalBlock(cipher, 0, cipher.Length);
363
}
364
string json = Encoding.UTF8.GetString(plain);
365
ClearBytes(aesKey);
366
ClearBytes(hmacKey);
367
ClearBytes(expected);
368
ClearBytes(plain);
369
ClearBytes(iv);
370
ClearBytes(cipher);
371
ClearBytes(salt);
372
ClearBytes(rotation);
373
return JsonUtility.FromJson<T>(json);
374
}
375
}
376
catch { return default; }
377
}
378
379
private static byte[] GenerateRandomBytes(int len)
380
{
381
byte[] b = new byte[len];
382
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(b);
383
return b;
384
}
385
386
private static void ClearBytes(byte[] b)
387
{
388
if (b == null) return;
389
Array.Clear(b, 0, b.Length);
390
}
391
392
private string ResolveFileName(string baseName)
393
{
394
if (!EnableHardening || !EnableFileNameObfuscation) return baseName + ".dat";
395
byte[] nameBytes = Encoding.UTF8.GetBytes(baseName + Application.identifier);
396
using (var sha = SHA256.Create())
397
{
398
byte[] hash = sha.ComputeHash(nameBytes);
399
string b64 = Convert.ToBase64String(hash);
400
string safe = b64.Replace('+', '-').Replace('/', '_').Replace('=', 'x');
401
ClearBytes(hash);
402
return safe.Substring(0, Math.Min(28, safe.Length)) + ".dat";
403
}
404
}
405
406
private bool IsTamperedOrDebug()
407
{
408
if (!EnableHardening) return false;
409
try
410
{
411
if (EnableAntiDebugChecks)
412
{
413
if (System.Diagnostics.Debugger.IsAttached) return true;
414
if (System.Diagnostics.Process.GetCurrentProcess().ProcessName.ToLower().Contains("debug")) return true;
415
}
416
}
417
catch { }
418
try
419
{
420
if (EnableRootDetection)
421
{
422
string[] suspectPaths = new string[] { "/system/bin/su", "/system/xbin/su", "/sbin/su" };
423
foreach (var p in suspectPaths) if (File.Exists(p)) return true;
424
}
425
}
426
catch { }
427
return false;
428
}
429
}
430
}}}
58431
=== 본 코드 ===
59432
{{{#!syntax csharp
60433
using System;
......