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