r200
r159
1[include(틀:유우리)]
r158
2[include(틀:스텔라이브)]
r163
3[include(틀:Aidenk/틀 모음, Aidenk=0)]
r85
4{{{#!wiki style="max-width: 500px; padding: 10px 0; border-radius: 20px; background: linear-gradient(to right, #ba9dff, #ba9dff); float: right; text-align: center"
r155
5||<-2><tablewidth=100%><table bgcolor=#fff,#1c1d1f><tablebordercolor=#ba9dff,#ba9dff><colcolor=#fff><color=#00d2ff> ||
r175
6||<-2><nopad>[youtube(EIii4EXsfsU)]||
r172
7||{{{#!folding 기존 사진 접기
r176
8[[파일:special gift of kanna.jpg|width=100%]][br][br][[파일:블아임.png|width=100%]][br][br][[파일:규리감.jpg|width=100%]][br][br][[파일:2025/09/27 유니버스 타비/리제 신의상 합방.jpg|width=100%]]}}}[br][[파일:클리셰 공식 단체 사진.jpg|width=100%]]||}}}
r56
9
r23
10[목차]
11[clearfix]
r167
12== 개요 ==
r168
13생방은 본지 몇개월 안된 유튜브로만 보던 1~2년차 파스텔. 초기의 시작은 강지와 김블루 우결. 그렇게 강지를 알게된후 칸나를 알게되어 이렇게 파스텔이 되었다 카더라.
r167
14
r196
15해둥이 주글께! - 해둥이 -
16
17주석은 혁명이야!
18
19[[스텔라이브]] 실시간 업데이트 중이진 않음
203기생 사진 준비중 ...
21
22직접 곡을 들으며 starr, end 시간대를 찾는 편집으로 힐링하는 사람. 편집도 하고 노래도 듣고 생방도 보고 너무 좋잖아!
23
24[[나무위키]]의 [[https://namu.wiki/w/%EC%82%AC%EC%9A%A9%EC%9E%90:Aidenk|Aidenk]], [[https://namu.wiki/w/%EC%82%AC%EC%9A%A9%EC%9E%90:Airi_Kanna|Airi_Kanna]], [[https://namu.wiki/w/%EC%82%AC%EC%9A%A9%EC%9E%90:Hebi|Hebi^^{{{-3 봇계정}}}^^]] 와 동일인. 더시드위키의 [[사용자:Tenko_Shibuki]], [[사용자:Shirayuki_Hina]]와 동일인.
25프로필 사진 너무 예쁜데... 예쁘고 귀여우니... 별찜좀...
26
27이나리로 전향한듯 한 해둥이(?)가 아닌데 이나리인데 해둥이. 근데 바람은 아님(?)
28음 그니깐... 쨋든 파스텔.
29
30[[/연습장|{{{#!wiki style="display: inline; color: transparent; background: text linear-gradient(to right, #f58abb, #fed09f)"
31'''연습장 바로가기'''}}}]]
32
33[[/버츄얼|{{{#!wiki style="display: inline; color: transparent; background: text linear-gradient(to right, #f58abb, #fed09f)"
34'''수필 바로가기'''}}}]]
35
r191
36== 통포(10/31) ==
r196
37 1. 위: [[시라유키 히나]] 13,700개
38 1. 위: [[텐코 시부키]] 10,480개
r174
39 1. 위: [[아라하시 타비]] 1,680개
40 1. 위: [[아카네 리제]] 1,560
41 1. 위: [[유즈하 리코]] 1,190
42 1. 위: [[하나코 나나]] 970개
r196
43 1. 위: [[네네코 마시로]] 690개
r174
44 1. 위: [[사키하네 후야]] 590개
45 1. 위: [[아오쿠모 린]] 460개
46 1. 위: [[아야츠노 유니]] 310개
47 1. 위: [[강지]] 300개
r167
48
r196
49==# 방송 역사 작성 요령 #==
r150
50간단합니다. 치지직에서 생방을 보면됩니다. 히나, 부키는 거기다가 방송에서 노래랑 피아노를 자주 쳐주고 불러주기에 힐링됩니다. 힐링 방송을 원하시다면 파스텔 또는 이나리, 해둥이가 되세요! 새벽 4시에 키는게 어디있냐고! 아... 자고싶다... 아닛! 휴대폰 키고 자버려서 노래방 못봤다...
r135
51
r143
52가끔 x 또는 카페의 방송 후기나 기록을 가지고 작성합니다. 유니, 타비, 시로, 리제, 린, 나나, 리코는 작성이 쉽습니다. 하지만 부키, 히나는 작성이 힘듭니다.
53히나는 "안자는 해둥이 있나요"는 새벽 3시에 끝나서 작성하기 힘듭니다. 하지만 저는 작성하죠 ㅎㅎ 부키는 평균 방송 시간 7시간 거의 새벽까지 방송하기에 히나와 비슷하게 새벽 3시까지는 깨어있어야합니다.
54
r184
55== csharp 암호화 코드 ==
r194
56GZipStream을 이용해서 저장 용량을 50% 줄여보기.
r184
57
r200
58
r198
59=== 본코드 ===
r182
60{{{#!syntax csharp
61using System;
62using System.IO;
63using System.Security.Cryptography;
64using System.Text;
65using UnityEngine;
66using System.Runtime.CompilerServices;
67
68[Serializable]
69public class PlayerData
70{
71 public string playerName;
72 public int score;
73}
74
75public class SecureManager : MonoBehaviour
76{
77 public static SecureManager Instance { get; private set; }
78
79 public bool EnableHardening = true;
80 public bool EnableAntiDebugChecks = true;
81 public bool EnableRootDetection = true;
r197
82 public bool EnableDummyMix = false;
83 public bool EnableFileNameObfuscation = true;
r182
84 public bool EnableRotation = true;
85
86 private const string Magic = "SJMP";
87 private const byte Version = 1;
88 private const int SaltLenDefault = 12;
89 private const int IvLen = 16;
90 private const int HmacLen = 32;
91 private const int KeyMaterialLen = 64;
92 private const int AesKeyLen = 32;
93 private const int HmacKeyLen = 32;
94 private const int Pbkdf2Iterations = 20000;
95 private readonly byte[] obfPartA = new byte[] { 0x4A, 0x5F, 0x33, 0x29, 0x11, 0x7C, 0x6D, 0x3E, 0x2D, 0x7A, 0x5B, 0x1C };
96 private readonly byte[] obfPartB = new byte[] { 0x91, 0x20, 0x55, 0x12, 0x44, 0x38, 0x77, 0x0A, 0x6F, 0x21, 0x9D, 0xEE };
97 private const byte Mask = 0xAA;
98 private string filePath;
99 private byte[] sessionNonce;
100
101 void Awake()
102 {
103 if (Instance != null && Instance != this)
104 {
105 Destroy(gameObject);
106 return;
107 }
108 Instance = this;
109 DontDestroyOnLoad(gameObject);
110
r198
111 sessionNonce = LoadOrCreateSessionNonce();
r182
112 filePath = Path.Combine(Application.persistentDataPath, ResolveFileName("playerData"));
113 Warmup();
114 }
115
r198
116 private byte[] LoadOrCreateSessionNonce()
117 {
118 const string key = "SecureManager_SessionNonce_v1";
119 try
120 {
121 if (PlayerPrefs.HasKey(key))
122 {
123 string b64 = PlayerPrefs.GetString(key);
124 byte[] stored = Convert.FromBase64String(b64);
125 if (stored != null && stored.Length > 0) return stored;
126 }
127 }
128 catch { }
129
130 byte[] nonce = GenerateRandomBytes(16);
131 try
132 {
133 PlayerPrefs.SetString(key, Convert.ToBase64String(nonce));
134 PlayerPrefs.Save();
135 }
136 catch { }
137 return nonce;
138 }
139
r182
140 private void Warmup()
141 {
142 try { _ = GetSecretBytes(); } catch { }
143 }
144
145 [MethodImpl(MethodImplOptions.NoInlining)]
146 private byte[] DumbMixA(byte[] input)
147 {
148 byte[] outb = new byte[input.Length];
149 for (int i = 0; i < input.Length; i++)
150 {
151 int v = input[i];
152 v = ((v << 3) | (v >> 5)) & 0xFF;
153 v ^= (i * 37) & 0xFF;
154 outb[i] = (byte)v;
155 }
156 return outb;
157 }
158
159 [MethodImpl(MethodImplOptions.NoInlining)]
160 private byte[] DumbMixB(byte[] input, int seed)
161 {
162 byte[] outb = new byte[input.Length];
163 for (int i = 0; i < input.Length; i++)
164 {
165 int v = input[i] ^ (seed >> (i % 8));
166 v = (v * 13 + (i * 7)) & 0xFF;
167 v = (v ^ 0x5A) & 0xFF;
168 outb[i] = (byte)v;
169 }
170 return outb;
171 }
172
173 [MethodImpl(MethodImplOptions.NoInlining)]
174 private byte[] DumbMixC(byte[] input, byte[] nonce)
175 {
176 byte[] outb = new byte[input.Length];
177 for (int i = 0; i < input.Length; i++)
178 {
179 int n = nonce[i % nonce.Length];
180 int v = input[i];
181 v = ((v + n) ^ (n >> (i % 4))) & 0xFF;
182 outb[i] = (byte)v;
183 }
184 return outb;
185 }
186
187 private byte[] GetSecretBytes()
188 {
189 byte[] k = new byte[obfPartA.Length + obfPartB.Length];
190 for (int i = 0; i < obfPartA.Length; i++) k[i] = (byte)(obfPartA[i] ^ Mask);
191 for (int i = 0; i < obfPartB.Length; i++) k[i + obfPartA.Length] = (byte)(obfPartB[i] ^ Mask);
192
193 if (!EnableHardening || !EnableDummyMix)
194 {
195 byte[] simple = new byte[k.Length];
196 Buffer.BlockCopy(k, 0, simple, 0, k.Length);
197 Array.Clear(k, 0, k.Length);
198 return simple;
199 }
200
201 byte[] s1 = DumbMixA(k);
r198
202
r199
203
r198
204 int deviceSeed = Application.identifier.GetHashCode() ^ SystemInfo.deviceUniqueIdentifier.GetHashCode();
205
206 byte[] s2 = DumbMixB(s1, deviceSeed);
r182
207 byte[] s3 = DumbMixC(s2, sessionNonce);
208 for (int r = 0; r < 2; r++)
209 {
210 byte[] t = DumbMixA(s3);
r198
211 byte[] u = DumbMixB(t, deviceSeed ^ r);
r182
212 for (int i = 0; i < k.Length; i++) s3[i] = (byte)(s3[i] ^ u[i % u.Length]);
213 Array.Clear(t, 0, t.Length);
214 Array.Clear(u, 0, u.Length);
215 }
216 Array.Clear(s1, 0, s1.Length);
217 Array.Clear(s2, 0, s2.Length);
r198
218
r182
219 byte[] result = new byte[k.Length];
220 for (int i = 0; i < k.Length; i++) result[i] = (byte)(k[i] ^ s3[i % s3.Length]);
221 Array.Clear(k, 0, k.Length);
222 Array.Clear(s3, 0, s3.Length);
223 return result;
224 }
225
226 private (byte[] aesKey, byte[] hmacKey) DeriveKeysRotating(byte[] salt, byte[] rotationNonce)
227 {
228 byte[] secret = GetSecretBytes();
229 string secretStr = EnableHardening ? Convert.ToBase64String(secret) + Application.identifier : Encoding.UTF8.GetString(secret) + Application.identifier;
230 Array.Clear(secret, 0, secret.Length);
231
232 using (var kdf = new Rfc2898DeriveBytes(secretStr, salt, Pbkdf2Iterations, HashAlgorithmName.SHA256))
233 {
234 byte[] km = kdf.GetBytes(KeyMaterialLen);
235 if (EnableHardening && EnableRotation && rotationNonce != null)
236 {
237 for (int i = 0; i < km.Length; i++) km[i] ^= rotationNonce[i % rotationNonce.Length];
238 }
239 byte[] aesKey = new byte[AesKeyLen];
240 byte[] hmacKey = new byte[HmacKeyLen];
241 Buffer.BlockCopy(km, 0, aesKey, 0, AesKeyLen);
242 Buffer.BlockCopy(km, AesKeyLen, hmacKey, 0, HmacKeyLen);
243 Array.Clear(km, 0, km.Length);
244 return (aesKey, hmacKey);
245 }
246 }
247
248 public void Save<T>(T data)
249 {
250 if (EnableHardening && (EnableAntiDebugChecks || EnableRootDetection) && IsTamperedOrDebug()) return;
251 try
252 {
253 string json = JsonUtility.ToJson(data);
254 byte[] salt = GenerateRandomBytes(SaltLenDefault);
255 byte[] rotation = (EnableHardening && EnableRotation) ? GenerateRandomBytes(16) : new byte[16];
256 var keys = DeriveKeysRotating(salt, (EnableRotation ? rotation : null));
257 byte[] aesKey = keys.aesKey;
258 byte[] hmacKey = keys.hmacKey;
259 byte[] plain = Encoding.UTF8.GetBytes(json);
260 byte[] iv;
261 byte[] cipher;
262 using (Aes aes = Aes.Create())
263 {
264 aes.KeySize = 256;
265 aes.Mode = CipherMode.CBC;
266 aes.Padding = PaddingMode.PKCS7;
267 aes.Key = aesKey;
268 aes.GenerateIV();
269 iv = aes.IV;
270 using (var enc = aes.CreateEncryptor(aes.Key, iv))
271 cipher = enc.TransformFinalBlock(plain, 0, plain.Length);
272 }
273 byte[] ivCipher = new byte[iv.Length + cipher.Length];
274 Buffer.BlockCopy(iv, 0, ivCipher, 0, iv.Length);
275 Buffer.BlockCopy(cipher, 0, ivCipher, iv.Length, cipher.Length);
276 byte[] hmac;
277 using (var h = new HMACSHA256(hmacKey)) hmac = h.ComputeHash(ivCipher);
278 using (MemoryStream ms = new MemoryStream())
279 {
280 ms.Write(Encoding.ASCII.GetBytes(Magic), 0, 4);
281 ms.WriteByte(Version);
282 ms.WriteByte((byte)salt.Length);
283 ms.Write(salt, 0, salt.Length);
284 ms.WriteByte(EnableRotation ? (byte)rotation.Length : (byte)0);
285 if (EnableRotation) ms.Write(rotation, 0, rotation.Length);
286 ms.Write(iv, 0, iv.Length);
287 byte[] cipherLenBytes = BitConverter.GetBytes((Int32)cipher.Length);
288 if (!BitConverter.IsLittleEndian) Array.Reverse(cipherLenBytes);
289 ms.Write(cipherLenBytes, 0, 4);
290 ms.Write(cipher, 0, cipher.Length);
291 ms.Write(hmac, 0, hmac.Length);
292 string outBlob = Convert.ToBase64String(ms.ToArray());
293 File.WriteAllText(filePath, outBlob);
294 }
295 ClearBytes(aesKey);
296 ClearBytes(hmacKey);
297 ClearBytes(plain);
298 ClearBytes(iv);
299 ClearBytes(cipher);
300 ClearBytes(ivCipher);
301 ClearBytes(hmac);
302 ClearBytes(salt);
303 ClearBytes(rotation);
304 }
305 catch { }
306 }
307
308 public T Load<T>()
309 {
310 if (EnableHardening && (EnableAntiDebugChecks || EnableRootDetection) && IsTamperedOrDebug()) return default;
311 if (!File.Exists(filePath)) return default;
312 string blob = File.ReadAllText(filePath);
313 byte[] total;
314 try { total = Convert.FromBase64String(blob); } catch { return default; }
315 try
316 {
317 using (MemoryStream ms = new MemoryStream(total))
318 using (BinaryReader br = new BinaryReader(ms))
319 {
320 byte[] magic = br.ReadBytes(4);
321 if (Encoding.ASCII.GetString(magic) != Magic) return default;
322 byte ver = br.ReadByte();
323 if (ver != Version) return default;
324 int saltLen = br.ReadByte();
325 if (saltLen <= 0 || saltLen > 64) return default;
326 byte[] salt = br.ReadBytes(saltLen);
327 int rotationLen = br.ReadByte();
328 byte[] rotation = rotationLen > 0 ? br.ReadBytes(rotationLen) : new byte[16];
329 byte[] iv = br.ReadBytes(IvLen);
330 int cipherLen = br.ReadInt32();
331 if (!BitConverter.IsLittleEndian) cipherLen = System.Net.IPAddress.NetworkToHostOrder(cipherLen);
332 if (cipherLen <= 0 || cipherLen > total.Length) return default;
333 byte[] cipher = br.ReadBytes(cipherLen);
334 byte[] hmac = br.ReadBytes(HmacLen);
335 byte[] ivCipher = new byte[iv.Length + cipher.Length];
336 Buffer.BlockCopy(iv, 0, ivCipher, 0, iv.Length);
337 Buffer.BlockCopy(cipher, 0, ivCipher, iv.Length, cipher.Length);
338 var keys = DeriveKeysRotating(salt, (EnableRotation ? rotation : null));
339 byte[] aesKey = keys.aesKey;
340 byte[] hmacKey = keys.hmacKey;
341 byte[] expected;
342 using (var h = new HMACSHA256(hmacKey)) expected = h.ComputeHash(ivCipher);
343 bool ok = CryptographicOperations.FixedTimeEquals(expected, hmac);
344 if (!ok)
345 {
346 ClearBytes(aesKey);
347 ClearBytes(hmacKey);
348 ClearBytes(expected);
349 return default;
350 }
351 byte[] plain;
352 using (Aes aes = Aes.Create())
353 {
354 aes.KeySize = 256;
355 aes.Mode = CipherMode.CBC;
356 aes.Padding = PaddingMode.PKCS7;
357 aes.Key = aesKey;
358 aes.IV = iv;
359 using (var dec = aes.CreateDecryptor(aes.Key, aes.IV))
360 plain = dec.TransformFinalBlock(cipher, 0, cipher.Length);
361 }
362 string json = Encoding.UTF8.GetString(plain);
363 ClearBytes(aesKey);
364 ClearBytes(hmacKey);
365 ClearBytes(expected);
366 ClearBytes(plain);
367 ClearBytes(iv);
368 ClearBytes(cipher);
369 ClearBytes(salt);
370 ClearBytes(rotation);
371 return JsonUtility.FromJson<T>(json);
372 }
373 }
374 catch { return default; }
375 }
376
377 private static byte[] GenerateRandomBytes(int len)
378 {
379 byte[] b = new byte[len];
380 using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(b);
381 return b;
382 }
383
384 private static void ClearBytes(byte[] b)
385 {
386 if (b == null) return;
387 Array.Clear(b, 0, b.Length);
388 }
389
390 private string ResolveFileName(string baseName)
391 {
392 if (!EnableHardening || !EnableFileNameObfuscation) return baseName + ".dat";
393 byte[] nameBytes = Encoding.UTF8.GetBytes(baseName + Application.identifier);
394 using (var sha = SHA256.Create())
395 {
396 byte[] hash = sha.ComputeHash(nameBytes);
397 string b64 = Convert.ToBase64String(hash);
398 string safe = b64.Replace('+', '-').Replace('/', '_').Replace('=', 'x');
399 ClearBytes(hash);
400 return safe.Substring(0, Math.Min(28, safe.Length)) + ".dat";
401 }
402 }
403
404 private bool IsTamperedOrDebug()
405 {
406 if (!EnableHardening) return false;
407 try
408 {
409 if (EnableAntiDebugChecks)
410 {
r198
411 if (System.Diagnostics.Debugger.IsAttached) return true;
412 if (System.Diagnostics.Process.GetCurrentProcess().ProcessName.ToLower().Contains("debug")) return true;
r182
413 }
414 }
415 catch { }
416 try
417 {
418 if (EnableRootDetection)
419 {
420 string[] suspectPaths = new string[] { "/system/bin/su", "/system/xbin/su", "/sbin/su" };
421 foreach (var p in suspectPaths) if (File.Exists(p)) return true;
422 }
423 }
424 catch { }
425 return false;
426 }
r198
427}
428}}}
r185
429=== 실행 코드 ===
r183
430{{{#!syntax csharp
431using UnityEngine;
432
r185
433/// <summary>
434/// SecureManager의 Save 및 Load 메서드를 시연하는 예시 스크립트입니다.
435/// </summary>
r183
436public class ExampleUsage : MonoBehaviour
437{
438 private readonly string testPlayerName = "Gemini_Test_User";
439 private readonly int initialScore = 1000;
440 private readonly int newScore = 5000;
441
442 void Start()
443 {
r185
444 // SecureManager가 씬에 존재하는지 확인합니다.
r183
445 if (SecureManager.Instance == null)
446 {
447 Debug.LogError("SecureManager 인스턴스를 찾을 수 없습니다. SecureManager.cs를 GameObject에 추가했는지 확인해주세요.");
448 return;
449 }
r185
450
451 // 1. 데이터 저장 테스트
r183
452 SaveTestData();
453
r185
454 // 2. 데이터 로드 테스트
r183
455 LoadTestData();
456
r185
457 // 3. 데이터 업데이트 및 재저장
r183
458 UpdateAndSaveTestData();
r185
459
460 // 4. 업데이트된 데이터 로드 확인
r183
461 LoadTestData();
462 }
463
464 void SaveTestData()
465 {
466 Debug.Log("--- 1. 초기 데이터 저장 시도 ---");
467 PlayerData dataToSave = new PlayerData
468 {
469 playerName = testPlayerName,
470 score = initialScore
471 };
472
473 SecureManager.Instance.Save(dataToSave);
474 Debug.Log($"초기 데이터 저장 완료: Player={dataToSave.playerName}, Score={dataToSave.score}");
475 }
476
477 void LoadTestData()
478 {
479 Debug.Log("--- 2. 데이터 로드 시도 ---");
480 PlayerData loadedData = SecureManager.Instance.Load<PlayerData>();
481
482 if (loadedData != null)
483 {
484 Debug.Log($"데이터 로드 성공: Player={loadedData.playerName}, Score={loadedData.score}");
485 }
486 else
487 {
488 Debug.LogWarning("데이터 로드 실패 (파일 없음, 오류 발생, 혹은 디버거 감지).");
489 }
490 }
491
492 void UpdateAndSaveTestData()
493 {
494 Debug.Log("--- 3. 데이터 업데이트 및 재저장 시도 ---");
495 PlayerData loadedData = SecureManager.Instance.Load<PlayerData>();
496
497 if (loadedData != null)
498 {
499 loadedData.score = newScore;
500 SecureManager.Instance.Save(loadedData);
501 Debug.Log($"데이터 업데이트 및 저장 완료: New Score={loadedData.score}");
502 }
503 }
504
505 void OnDestroy()
506 {
r185
507 // 애플리케이션 종료 시 디버거 체크가 활성화된 경우 에러 방지
r183
508 if (SecureManager.Instance != null && SecureManager.Instance.EnableAntiDebugChecks)
509 {
r187
510 SecureManager.Instance.EnableAntiDebugChecks = false;
r183
511 Debug.Log("SecureManager: Anti-Debug Checks가 활성화되어 있습니다.");
512 }
513 }
r182
514} }}}